diff --git a/SigMaker/Main.cpp b/SigMaker/Main.cpp
index 20a62bc..36a8893 100644
--- a/SigMaker/Main.cpp
+++ b/SigMaker/Main.cpp
@@ -93,17 +93,17 @@ bool idaapi run( size_t /*arg*/ )
return true;
}
-int idaapi init( void )
+plugmod_t* idaapi init( void )
{
Settings.Init( );
Settings.Load( "sigmaker.ini" );
- return PLUGIN_OK;
+ return PLUGIN_KEEP;
}
plugin_t PLUGIN = {
IDP_INTERFACE_VERSION,
- PLUGIN_KEEP,
+ 0,// PLUGIN_KEEP,
init,
NULL,
run,
diff --git a/SigMaker/SigMaker.vcxproj b/SigMaker/SigMaker.vcxproj
index 5e50a16..25cabfb 100644
--- a/SigMaker/SigMaker.vcxproj
+++ b/SigMaker/SigMaker.vcxproj
@@ -22,34 +22,34 @@
{60916877-60AB-4565-93BC-2D6097976D86}
Win32Proj
SigMaker
- 10.0.17134.0
+ 10.0
DynamicLibrary
true
Unicode
- v141
+ v142
DynamicLibrary
true
Unicode
- v141
+ v142
DynamicLibrary
false
true
Unicode
- v141
+ v142
DynamicLibrary
false
true
Unicode
- v141
+ v142
@@ -90,7 +90,7 @@
Level3
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;SIGMAKER_EXPORTS;%(PreprocessorDefinitions)
- $(IDADIR)\idasdk\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)idasdk75\include\;%(AdditionalIncludeDirectories)
false
@@ -114,7 +114,7 @@
Level3
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;__EA64__;SIGMAKER_EXPORTS;%(PreprocessorDefinitions)
- $(IDADIR)\idasdk\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)idasdk75\include\;%(AdditionalIncludeDirectories)
false
@@ -139,7 +139,7 @@
false
MultiThreadedDLL
false
- $(IDADIR)\idasdk\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)idasdk75\include\;%(AdditionalIncludeDirectories)
true
false
@@ -179,7 +179,7 @@
MultiThreadedDLL
false
false
- $(IDADIR)\idasdk\include;%(AdditionalIncludeDirectories)
+ $(SolutionDir)idasdk75\include\;%(AdditionalIncludeDirectories)
false
@@ -191,16 +191,18 @@
/EXPORT:PLUGIN %(AdditionalOptions)
false
false
- $(IDADIR)\idasdk\lib\x64_win_vc_64;%(AdditionalLibraryDirectories)
+ $(SolutionDir)idasdk75\lib\x64_win_vc_64;%(AdditionalLibraryDirectories)
false
- copy "$(TargetDir)$(TargetName)$(TargetExt)" "$(IDADIR)\plugins\sigmaker64.dll"
+
+
- copy "$(TargetDir)$(TargetName)$(TargetExt)" "$(IDADIR)\plugins\sigmaker64.dll"
+
+
diff --git a/idasdk75/allmake.mak b/idasdk75/allmake.mak
new file mode 100644
index 0000000..74174cc
--- /dev/null
+++ b/idasdk75/allmake.mak
@@ -0,0 +1,823 @@
+#
+# 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:=5
+# 750
+IDAVERDECIMAL:=$(IDAVER_MAJOR)$(IDAVER_MINOR)0
+# 7.5
+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
+
+# disable x86 ida64 builds
+ifeq ($(or $(MM32),$(MMO32),$(MMSO32)),1)
+ $(error x86 ida64 builds have been disabled)
+endif
+
+ifdef __ARM__
+ PROCDEF = __ARM__
+ TARGET_PROCESSOR_NAME=arm
+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 += $(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
+
+ # 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
+
+ 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))
+
+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
+ EXTRASUF=_home
+ IDAHOME_PROCESSORS=pc arm ppc mips mc68k
+else
+ ifdef USE_STATIC_RUNTIME
+ EXTRASUF=_s
+ endif
+endif
+
+#############################################################################
+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:
diff --git a/idasdk75/dbg/arm_debmod.cpp b/idasdk75/dbg/arm_debmod.cpp
new file mode 100644
index 0000000..0f43a53
--- /dev/null
+++ b/idasdk75/dbg/arm_debmod.cpp
@@ -0,0 +1,294 @@
+#include
+#include
+#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
+
+
+#include "arm_regs.hpp"
+#include "deb_arm.hpp"
+
+//--------------------------------------------------------------------------
+arm_debmod_t::arm_debmod_t()
+{
+ 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);
+
+ is_xscale = false;
+ for ( size_t i = 0; i < 2; i++ )
+ {
+ databpts[i] = BADADDR;
+ codebpts[i] = BADADDR;
+ dbptypes[i] = -1;
+ cbptypes[i] = -1;
+ }
+ dbcon = 0;
+ set_platform("linux");
+}
+
+//--------------------------------------------------------------------------
+int idaapi arm_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t /*ea*/, int len)
+{
+ if ( type == BPT_SOFT )
+ return BPT_OK;
+
+ if ( !is_xscale )
+ return BPT_BAD_TYPE; // hardware bpts are supported only for xScale
+
+ // For some reason hardware instruction breakpoints do not work
+ if ( type == BPT_EXEC )
+ return BPT_BAD_TYPE;
+
+ if ( len > 4 )
+ return BPT_BAD_LEN;
+
+ bool ok = databpts[0] == BADADDR || databpts[1] == BADADDR;
+
+ return ok ? BPT_OK : BPT_TOO_MANY;
+}
+
+//--------------------------------------------------------------------------
+bool arm_debmod_t::add_hwbpt(bpttype_t type, ea_t ea, int len)
+{
+ // msg("add_hwbpt %d %a %d\n", type, ea, len);
+ if ( !is_xscale || len > 4 )
+ return false;
+
+ if ( !init_hwbpt_support() )
+ return false;
+
+ if ( type == BPT_EXEC )
+ {
+ if ( codebpts[0] != BADADDR && codebpts[1] != BADADDR )
+ return false;
+
+ int slot = codebpts[0] != BADADDR;
+ codebpts[slot] = ea;
+ cbptypes[slot] = type;
+ }
+ else
+ {
+ if ( databpts[0] != BADADDR && databpts[1] != BADADDR )
+ return false;
+
+ int slot = databpts[0] != BADADDR;
+ int bits;
+ switch ( type )
+ {
+ case BPT_WRITE:
+ bits = 1; // store only
+ break;
+ case BPT_RDWR:
+ bits = 2; // load/store
+ break;
+ // BPT_READ: // load only
+ // bits = 3;
+ // break;
+ default:
+ return false;
+ }
+ databpts[slot] = ea;
+ dbptypes[slot] = type;
+ dbcon |= bits << (slot*2);
+ }
+ return enable_hwbpts();
+}
+
+//--------------------------------------------------------------------------
+bool arm_debmod_t::del_hwbpt(ea_t ea, bpttype_t type)
+{
+ // msg("del_hwbpt %a\n", ea);
+ if ( databpts[0] == ea && dbptypes[0] == type )
+ {
+ databpts[0] = BADADDR;
+ dbcon &= ~3;
+ }
+ else if ( databpts[1] == ea && dbptypes[1] == type )
+ {
+ databpts[1] = BADADDR;
+ dbcon &= ~(3<<2);
+ }
+ else if ( codebpts[0] == ea && cbptypes[0] == type )
+ {
+ codebpts[0] = BADADDR;
+ }
+ else if ( codebpts[1] == ea && cbptypes[1] == type )
+ {
+ codebpts[1] = BADADDR;
+ }
+ else
+ {
+ return false;
+ }
+ return enable_hwbpts();
+}
+
+//--------------------------------------------------------------------------
+void arm_debmod_t::cleanup_hwbpts()
+{
+ databpts[0] = BADADDR;
+ databpts[1] = BADADDR;
+ codebpts[0] = BADADDR;
+ codebpts[1] = BADADDR;
+ dbcon = 0;
+ // disable all bpts
+ if ( is_xscale )
+ disable_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;
+ }
+}
diff --git a/idasdk75/dbg/arm_debmod.h b/idasdk75/dbg/arm_debmod.h
new file mode 100644
index 0000000..f003afa
--- /dev/null
+++ b/idasdk75/dbg/arm_debmod.h
@@ -0,0 +1,66 @@
+#ifndef __ARM_DEBMOD__
+#define __ARM_DEBMOD__
+
+#include "deb_arm.hpp"
+#include "debmod.h"
+
+//--------------------------------------------------------------------------
+class arm_debmod_t : public debmod_t
+{
+ typedef debmod_t inherited;
+protected:
+ bool is_xscale;
+ ea_t databpts[2];
+ ea_t codebpts[2];
+ bpttype_t dbptypes[2];
+ bpttype_t cbptypes[2];
+ int dbcon;
+
+ int lr_idx;
+ int sr_idx;
+
+public:
+ arm_debmod_t();
+ void cleanup_hwbpts();
+
+ bool del_hwbpt(ea_t ea, bpttype_t type);
+ bool add_hwbpt(bpttype_t type, ea_t ea, int len);
+ ea_t is_hwbpt_triggered(thid_t id, bool is_stepping);
+
+ inline bool active_databpts(void)
+ {
+ return databpts[0] != BADADDR || databpts[1] != BADADDR;
+ }
+
+ inline bool active_codebpts(void)
+ {
+ return codebpts[0] != BADADDR || codebpts[1] != BADADDR;
+ }
+
+ inline bool active_hwbpts(void)
+ {
+ return active_databpts() || active_codebpts();
+ }
+
+ // 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;
+
+ // new virtial functions
+ virtual bool init_hwbpt_support() newapi { return true; }
+ virtual bool disable_hwbpts() newapi { return false; }
+ virtual bool enable_hwbpts() newapi { return false; }
+ virtual bool refresh_hwbpts() newapi { return false; }
+
+ 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
diff --git a/idasdk75/dbg/arm_local_impl.cpp b/idasdk75/dbg/arm_local_impl.cpp
new file mode 100644
index 0000000..742784a
--- /dev/null
+++ b/idasdk75/dbg/arm_local_impl.cpp
@@ -0,0 +1,107 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "deb_arm.hpp"
+
+#include "arm_regs.cpp"
+
+//--------------------------------------------------------------------------
+int is_arm_valid_bpt(bpttype_t type, ea_t ea, int len)
+{
+ if ( type == BPT_SOFT )
+ {
+ if ( (ea & 1) != 0 )
+ return BPT_BAD_ADDR;
+ }
+ else
+ {
+ if ( type != BPT_RDWR // type is good?
+ && type != BPT_WRITE
+ && type != BPT_EXEC )
+ {
+ return BPT_BAD_TYPE;
+ }
+
+ if ( (ea & (len-1)) != 0 ) // alignment is good?
+ return BPT_BAD_ALIGN;
+
+ if ( len != 1 )
+ {
+ warning("AUTOHIDE REGISTRY\n"
+ "xScale supports only 1 byte length hardware breakpoints");
+ return BPT_BAD_LEN;
+ }
+ }
+ 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();
+}
diff --git a/idasdk75/dbg/arm_regs.cpp b/idasdk75/dbg/arm_regs.cpp
new file mode 100644
index 0000000..0399eb9
--- /dev/null
+++ b/idasdk75/dbg/arm_regs.cpp
@@ -0,0 +1,213 @@
+
+#include "arm_regs.hpp"
+
+//-------------------------------------------------------------------------
+// NOTE: keep in sync with register_class_arm_t
+const char *arm_register_classes[] =
+{
+ "General registers",
+ "VFP 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
+};
+#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 },
+#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;
+}
diff --git a/idasdk75/dbg/arm_regs.hpp b/idasdk75/dbg/arm_regs.hpp
new file mode 100644
index 0000000..ec39797
--- /dev/null
+++ b/idasdk75/dbg/arm_regs.hpp
@@ -0,0 +1,136 @@
+
+#pragma once
+
+#include
+#include
+
+//-------------------------------------------------------------------------
+#if defined(__LINUX__) && defined(__ARM__) && !defined(__EA64__)
+# define __HAVE_ARM_VFP__
+#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_ALL = ARM_RC_GENERAL
+#ifdef __HAVE_ARM_VFP__
+ | ARM_RC_VFP
+#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,
+#endif
+};
+
+// Number of registers in arm and aarch64
+#define ARM64_NREGS 34
+#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);
diff --git a/idasdk75/dbg/bin_search.cpp b/idasdk75/dbg/bin_search.cpp
new file mode 100644
index 0000000..00a67ec
--- /dev/null
+++ b/idasdk75/dbg/bin_search.cpp
@@ -0,0 +1,906 @@
+#include
+#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 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 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:
+ DECLARE_VIRTUAL_DTOR(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_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
diff --git a/idasdk75/dbg/bochs/bochsext.h b/idasdk75/dbg/bochs/bochsext.h
new file mode 100644
index 0000000..c0dee72
--- /dev/null
+++ b/idasdk75/dbg/bochs/bochsext.h
@@ -0,0 +1,34 @@
+/*
+ * Interactive disassembler (IDA).
+ * ALL RIGHTS RESERVED.
+ * Copyright (c) 1990-2020 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
diff --git a/idasdk75/dbg/bochs/ctrl/bochsys.h b/idasdk75/dbg/bochs/ctrl/bochsys.h
new file mode 100644
index 0000000..96c0188
--- /dev/null
+++ b/idasdk75/dbg/bochs/ctrl/bochsys.h
@@ -0,0 +1,62 @@
+/*
+ * Interactive disassembler (IDA).
+ * ALL RIGHTS RESERVED.
+ * Copyright (c) 1990-2020 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
+
+#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
diff --git a/idasdk75/dbg/bochs/ctrl/startup.idc b/idasdk75/dbg/bochs/ctrl/startup.idc
new file mode 100644
index 0000000..accd9ca
--- /dev/null
+++ b/idasdk75/dbg/bochs/ctrl/startup.idc
@@ -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 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;
+}
+
diff --git a/idasdk75/dbg/bochs/sdk/api_user32.idc b/idasdk75/dbg/bochs/sdk/api_user32.idc
new file mode 100644
index 0000000..459d6fb
--- /dev/null
+++ b/idasdk75/dbg/bochs/sdk/api_user32.idc
@@ -0,0 +1,2 @@
+
+///func=MessageBoxA entry=bxtest.MyMessageBox
diff --git a/idasdk75/dbg/bochs/sdk/bxtest.c b/idasdk75/dbg/bochs/sdk/bxtest.c
new file mode 100644
index 0000000..c76ecdf
--- /dev/null
+++ b/idasdk75/dbg/bochs/sdk/bxtest.c
@@ -0,0 +1,89 @@
+#include "bochsys.h"
+#include
+
+//--------------------------------------------------------------------------
+// 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;
+}
diff --git a/idasdk75/dbg/bochs/sdk/bxtest.def b/idasdk75/dbg/bochs/sdk/bxtest.def
new file mode 100644
index 0000000..e3de877
--- /dev/null
+++ b/idasdk75/dbg/bochs/sdk/bxtest.def
@@ -0,0 +1,4 @@
+EXPORTS
+ MyMessageBox
+ MyRoutine
+ MyR0Entry
\ No newline at end of file
diff --git a/idasdk75/dbg/bochs/sdk/bxtest.dll b/idasdk75/dbg/bochs/sdk/bxtest.dll
new file mode 100644
index 0000000..f16d424
Binary files /dev/null and b/idasdk75/dbg/bochs/sdk/bxtest.dll differ
diff --git a/idasdk75/dbg/bochs/sdk/compile.bat b/idasdk75/dbg/bochs/sdk/compile.bat
new file mode 100644
index 0000000..bb107b5
--- /dev/null
+++ b/idasdk75/dbg/bochs/sdk/compile.bat
@@ -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
\ No newline at end of file
diff --git a/idasdk75/dbg/bochs/sdk/readme.txt b/idasdk75/dbg/bochs/sdk/readme.txt
new file mode 100644
index 0000000..c8ad123
--- /dev/null
+++ b/idasdk75/dbg/bochs/sdk/readme.txt
@@ -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());
+}
diff --git a/idasdk75/dbg/bochs/sdk/test.asm b/idasdk75/dbg/bochs/sdk/test.asm
new file mode 100644
index 0000000..4f6bd0e
--- /dev/null
+++ b/idasdk75/dbg/bochs/sdk/test.asm
@@ -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
\ No newline at end of file
diff --git a/idasdk75/dbg/bochs/sdk/test.pe b/idasdk75/dbg/bochs/sdk/test.pe
new file mode 100644
index 0000000..51d601d
Binary files /dev/null and b/idasdk75/dbg/bochs/sdk/test.pe differ
diff --git a/idasdk75/dbg/common_local_impl.cpp b/idasdk75/dbg/common_local_impl.cpp
new file mode 100644
index 0000000..7e60060
--- /dev/null
+++ b/idasdk75/dbg/common_local_impl.cpp
@@ -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
+#include
+#include
+
+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 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
+
+// arm has no single step mechanism
+// DBG_HAS_SET_RESUME_MODE must be set before init_debugger
+#if TARGET_PROCESSOR == PLFM_ARM
+# 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
+};
diff --git a/idasdk75/dbg/common_stub_impl.cpp b/idasdk75/dbg/common_stub_impl.cpp
new file mode 100644
index 0000000..4305e4d
--- /dev/null
+++ b/idasdk75/dbg/common_stub_impl.cpp
@@ -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
+#include
+#include
+#include
+#include
+
+//---------------------------------------------------------------------------
+//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
+}
diff --git a/idasdk75/dbg/dbg_pe_hlp.cpp b/idasdk75/dbg/dbg_pe_hlp.cpp
new file mode 100644
index 0000000..9533c99
--- /dev/null
+++ b/idasdk75/dbg/dbg_pe_hlp.cpp
@@ -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
+
+
diff --git a/idasdk75/dbg/dbg_rpc_client.cpp b/idasdk75/dbg/dbg_rpc_client.cpp
new file mode 100644
index 0000000..456de35
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_client.cpp
@@ -0,0 +1,246 @@
+
+// This file is included in the debugger stub that runs on the computer with IDA
+
+#include
+#include
+#include
+
+#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();
+}
diff --git a/idasdk75/dbg/dbg_rpc_client.h b/idasdk75/dbg/dbg_rpc_client.h
new file mode 100644
index 0000000..7d25576
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_client.h
@@ -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
diff --git a/idasdk75/dbg/dbg_rpc_engine.cpp b/idasdk75/dbg/dbg_rpc_engine.cpp
new file mode 100644
index 0000000..81d9daa
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_engine.cpp
@@ -0,0 +1,97 @@
+
+#include
+
+#include "dbg_rpc_engine.h"
+
+//-------------------------------------------------------------------------
+dbg_rpc_engine_t::dbg_rpc_engine_t(bool _is_client)
+ : rpc_engine_t(_is_client),
+ has_pending_event(false),
+ poll_debug_events(false)
+{
+}
+
+//--------------------------------------------------------------------------
+// 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;
+}
diff --git a/idasdk75/dbg/dbg_rpc_engine.h b/idasdk75/dbg/dbg_rpc_engine.h
new file mode 100644
index 0000000..7147c50
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_engine.h
@@ -0,0 +1,37 @@
+#ifndef __DBG_RPC_ENGINE__
+#define __DBG_RPC_ENGINE__
+
+#include
+#include
+#include
+
+//-------------------------------------------------------------------------
+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__
diff --git a/idasdk75/dbg/dbg_rpc_handler.cpp b/idasdk75/dbg/dbg_rpc_handler.cpp
new file mode 100644
index 0000000..52a4cc5
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_handler.cpp
@@ -0,0 +1,1418 @@
+#include
+
+#include
+#include
+#include
+#include // otherwise cannot compile win32_remote.bpr
+#include
+
+#include "server.h"
+
+//--------------------------------------------------------------------------
+// another copy of this function (for local debugging) is defined in common_local_impl.cpp
+int send_ioctl(
+ rpc_engine_t *srv,
+ int fn,
+ const void *buf,
+ size_t size,
+ void **poutbuf,
+ ssize_t *poutsize)
+{
+ return srv->send_ioctl(fn, buf, size, poutbuf, poutsize);
+}
+
+//--------------------------------------------------------------------------
+AS_PRINTF(3, 0) ssize_t dvmsg(int code, rpc_engine_t *rpc, const char *format, va_list va)
+{
+ if ( code == 0 )
+ code = RPC_MSG;
+ else if ( code > 0 )
+ code = RPC_WARNING;
+ else
+ code = RPC_ERROR;
+
+ bytevec_t req = prepare_rpc_packet((uchar)code);
+
+ char buf[MAXSTR];
+ qvsnprintf(buf, sizeof(buf), format, va);
+ req.pack_str(buf);
+
+ qfree(rpc->send_request_and_receive_reply(req));
+ if ( code == RPC_ERROR )
+ exit(1);
+ return strlen(buf);
+}
+
+//--------------------------------------------------------------------------
+void report_idc_error(rpc_engine_t *rpc, ea_t ea, error_t code, ssize_t errval, const char *errprm)
+{
+ if ( code == eOS )
+ {
+ dbg_rpc_handler_t *h = (dbg_rpc_handler_t *)rpc;
+ errval = h->get_debugger_instance()->get_system_specific_errno();
+ }
+
+ bytevec_t req = prepare_rpc_packet(RPC_REPORT_IDC_ERROR);
+ req.pack_ea64(ea);
+ req.pack_dd(code);
+ if ( (const char *)errval == errprm )
+ {
+ req.pack_db(1);
+ req.pack_str(errprm);
+ }
+ else
+ {
+ req.pack_db(0);
+ req.pack_ea64(errval);
+ }
+ qfree(rpc->send_request_and_receive_reply(req));
+}
+
+//--------------------------------------------------------------------------
+debmod_t *dbg_rpc_handler_t::get_debugger_instance()
+{
+ return dbg_mod; //lint !e1535 !e1536 exposes lower access member
+}
+
+//--------------------------------------------------------------------------
+void dbg_rpc_handler_t::prepare_broken_connection(void)
+{
+ if ( debmod_t::reuse_broken_connections )
+ {
+ if ( !dbg_mod->dbg_prepare_broken_connection() )
+ dmsg("Error preparing debugger server to handle a broken connection\n");
+ }
+}
+
+//--------------------------------------------------------------------------
+// dbg_rpc_handler_t
+//--------------------------------------------------------------------------
+dbg_rpc_handler_t::dbg_rpc_handler_t(
+ idarpc_stream_t *_irs,
+ dbgsrv_dispatcher_t *_dispatcher)
+ : client_handler_t(_irs, /*_verbose=*/ false),
+ dbg_rpc_engine_t(/*is_client=*/ false),
+ dbg_mod(NULL),
+ dispatcher(_dispatcher)
+{
+ clear_channels(); //lint -esym(1566,dbg_rpc_handler_t::channels) not inited
+ struct ida_local lambda_t
+ {
+ static int ioctl(rpc_engine_t *rpc, int fn, const void *buf, size_t size, void **out, ssize_t *outsz)
+ {
+ dbg_rpc_handler_t *serv = (dbg_rpc_handler_t *) rpc;
+ memory_deserializer_t mmdsr(buf, size);
+ if ( fn >= MIN_SERVER_IOCTL )
+ return serv->handle_server_ioctl(fn, out, outsz, mmdsr);
+ else
+ return serv->get_debugger_instance()->handle_ioctl(fn, buf, size, out, outsz);
+ }
+
+ static progress_loop_ctrl_t recv_data_iter(bool, size_t, size_t, void *ud)
+ {
+ dbg_rpc_handler_t *eng = (dbg_rpc_handler_t *) ud;
+ bool performed = false;
+ int code = eng->on_recv_packet_progress(&performed);
+ if ( performed )
+ return code == 0 ? plc_skip_iter : plc_cancel;
+ else
+ return plc_proceed;
+ }
+ };
+
+ set_ioctl_handler(lambda_t::ioctl);
+ irs_set_progress_cb(irs, 100, lambda_t::recv_data_iter, this);
+}
+
+//--------------------------------------------------------------------------
+dbg_rpc_handler_t::~dbg_rpc_handler_t()
+{
+ //lint -e(1506) Call to virtual function 'dbg_rpc_handler_t::get_broken_connection(void)' within a constructor or destructor
+ if ( !get_broken_connection() )
+ delete dbg_mod; // the connection is not broken, delete the debugger instance
+
+ //lint -esym(1579,dbg_rpc_handler_t::dbg_mod) pointer member might have been freed by a separate function
+ clear_channels();
+
+ dispatcher = NULL;
+}
+
+//------------------------------------------------------------------------
+// Function safe against time slicing attack, comparing time depends only on str length
+static bool password_matches(const char *str, const char *pass)
+{
+ if ( str == NULL )
+ return false;
+ int str_length = strlen(str);
+ int pass_length = strlen(pass);
+ int res = str_length ^ pass_length;
+ if ( pass_length != 0 )
+ {
+ for ( int i = 0; i < str_length; i++ )
+ res |= (pass[i % pass_length] ^ str[i]);
+ }
+ return res == 0;
+}
+
+//-------------------------------------------------------------------------
+bool dbg_rpc_handler_t::handle()
+{
+ bytevec_t req = prepare_rpc_packet(RPC_OPEN);
+ req.pack_dd(IDD_INTERFACE_VERSION);
+ req.pack_dd(DEBUGGER_ID);
+ req.pack_dd(sizeof(ea_t));
+
+ bool send_response = false;
+ rpc_packet_t *rp = send_request_and_receive_reply(req, PREQ_MUST_LOGIN);
+ bool ok = rp != NULL;
+ if ( ok )
+ {
+ send_response = true;
+
+ // Answer is after rpc_packet_t
+ memory_deserializer_t mmdsr(rp+1, rp->length);
+ ok = mmdsr.unpack_dd() != 0;
+ if ( !ok )
+ {
+ lprintf("[%d] Incompatible IDA version\n", session_id);
+ send_response = false;
+ }
+ else if ( !dispatcher->server_password.empty() )
+ {
+ const char *pass = mmdsr.unpack_str();
+ if ( !password_matches(pass, dispatcher->server_password.c_str()) )
+ {
+ lprintf("[%d] Bad password\n", session_id);
+ ok = false;
+ }
+ }
+ logged_in = ok;
+
+ qfree(rp);
+ }
+ else
+ {
+ lprintf("[%d] Could not establish the connection\n", session_id);
+ }
+
+ if ( send_response )
+ {
+ req = prepare_rpc_packet(RPC_OK);
+ req.pack_dd(ok);
+ send_data(req);
+
+ // remove reception timeout on the server side
+ recv_timeout = -1;
+ logged_in = true;
+
+ if ( ok )
+ {
+ // the main loop: handle client requests until it drops the connection
+ // or sends us RPC_OK (see rpc_debmod_t::close_remote)
+ bytevec_t empty;
+ rpc_packet_t *packet = send_request_and_receive_reply(empty);
+ if ( packet != NULL )
+ qfree(packet);
+ }
+ }
+ network_error = false;
+
+ bool preserve_server = false;
+ if ( get_broken_connection() )
+ {
+ if ( dispatcher->on_broken_conn == BCH_KEEP_DEBMOD )
+ {
+ term_irs();
+ lprintf("[%d] Debugged session entered into sleeping mode\n", session_id);
+ prepare_broken_connection();
+ preserve_server = true;
+ }
+ else
+ {
+ if ( dispatcher->on_broken_conn == BCH_KILL_PROCESS )
+ {
+ int pid = get_debugger_instance()->pid;
+ if ( pid > 0 )
+ {
+ lprintf("[%d] Killing debugged process %d\n",
+ session_id, get_debugger_instance()->pid);
+ int code = kill_process();
+ if ( code != 0 )
+ lprintf("[%d] Failed to kill process after %d seconds. Giving up\n",
+ session_id, code);
+ }
+ }
+ goto TERM_DEBMOD;
+ }
+ }
+ else
+ {
+TERM_DEBMOD:
+ get_debugger_instance()->dbg_term();
+ term_irs();
+ }
+
+ return !preserve_server;
+}
+
+//--------------------------------------------------------------------------
+void dbg_rpc_handler_t::set_debugger_instance(debmod_t *instance)
+{
+ dbg_mod = instance;
+ dbg_mod->rpc = this;
+}
+
+//--------------------------------------------------------------------------
+#ifdef VERBOSE_ENABLED
+static const char *bptcode2str(uint code)
+{
+ static const char *const strs[] =
+ {
+ "BPT_OK",
+ "BPT_INTERNAL_ERR",
+ "BPT_BAD_TYPE",
+ "BPT_BAD_ALIGN",
+ "BPT_BAD_ADDR",
+ "BPT_BAD_LEN",
+ "BPT_TOO_MANY",
+ "BPT_READ_ERROR",
+ "BPT_WRITE_ERROR",
+ "BPT_SKIP",
+ "BPT_PAGE_OK",
+ };
+ if ( code >= qnumber(strs) )
+ return "?";
+ return strs[code];
+}
+#endif
+
+//--------------------------------------------------------------------------
+int dbg_rpc_handler_t::rpc_update_bpts(bytevec_t &req, memory_deserializer_t &mmdsr)
+{
+ update_bpt_vec_t bpts;
+ int nadd = mmdsr.unpack_dd();
+ int ndel = mmdsr.unpack_dd();
+
+ if ( nadd < 0 || ndel < 0 || INT_MAX - ndel < nadd )
+ {
+ req.pack_dd(0);
+ verb(("update_bpts(nadd=%d, ndel=%d) => 0 (incorrect values)\n", nadd, ndel));
+ return 0;
+ }
+
+ bpts.resize(nadd+ndel);
+ ea_t ea = 0;
+ update_bpt_vec_t::iterator b;
+ update_bpt_vec_t::iterator bend = bpts.begin() + nadd;
+ for ( b=bpts.begin(); b != bend; ++b )
+ {
+ b->code = BPT_OK;
+ b->ea = ea + mmdsr.unpack_ea64(); ea = b->ea;
+ b->size = mmdsr.unpack_dd();
+ b->type = mmdsr.unpack_dd();
+ b->pid = mmdsr.unpack_dd();
+ b->tid = mmdsr.unpack_dd();
+ }
+
+ ea = 0;
+ bend += ndel;
+ for ( ; b != bend; ++b )
+ {
+ b->ea = ea + mmdsr.unpack_ea64(); ea = b->ea;
+ uchar len = mmdsr.unpack_db();
+ if ( len > 0 )
+ {
+ b->orgbytes.resize(len);
+ mmdsr.unpack_obj(b->orgbytes.begin(), len);
+ }
+ b->type = mmdsr.unpack_dd();
+ b->pid = mmdsr.unpack_dd();
+ b->tid = mmdsr.unpack_dd();
+ }
+
+#ifdef VERBOSE_ENABLED
+ for ( b=bpts.begin()+nadd; b != bend; ++b )
+ verb(("del_bpt(ea=%a, type=%d orgbytes.size=%" FMT_Z " size=%d)\n",
+ b->ea, b->type, b->orgbytes.size(), b->type != BPT_SOFT ? b->size : 0));
+#endif
+
+ int nbpts;
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_update_bpts(&nbpts, bpts.begin(), nadd, ndel, &errbuf);
+
+ bend = bpts.begin() + nadd;
+#ifdef VERBOSE_ENABLED
+ for ( b=bpts.begin(); b != bend; ++b )
+ verb(("add_bpt(ea=%a type=%d len=%d) => %s\n", b->ea, b->type, b->size, bptcode2str(b->code)));
+#endif
+
+ req.pack_dd(drc);
+ req.pack_dd(nbpts);
+ for ( b=bpts.begin(); b != bend; ++b )
+ {
+ req.pack_db(b->code);
+ if ( b->code == BPT_OK && b->type == BPT_SOFT )
+ {
+ req.pack_db(b->orgbytes.size());
+ req.append(b->orgbytes.begin(), b->orgbytes.size());
+ }
+ }
+
+ bend += ndel;
+ for ( ; b != bend; ++b )
+ {
+ req.pack_db(b->code);
+ verb(("del_bpt(ea=%a) => %s\n", b->ea, bptcode2str(b->code)));
+ }
+
+ if ( drc != DRC_OK )
+ req.pack_str(errbuf);
+ return drc;
+}
+
+//--------------------------------------------------------------------------
+void dbg_rpc_handler_t::rpc_update_lowcnds(
+ bytevec_t &req,
+ memory_deserializer_t &mmdsr)
+{
+ ea_t ea = 0;
+ lowcnd_vec_t lowcnds;
+ int nlowcnds = mmdsr.unpack_dd();
+ lowcnds.resize(nlowcnds);
+ lowcnd_t *lc = lowcnds.begin();
+ for ( int i=0; i < nlowcnds; i++, lc++ )
+ {
+ lc->compiled = false;
+ lc->ea = ea + mmdsr.unpack_ea64(); ea = lc->ea;
+ lc->cndbody = mmdsr.unpack_str();
+ if ( !lc->cndbody.empty() )
+ {
+ lc->size = 0;
+ lc->type = mmdsr.unpack_dd();
+ if ( lc->type != BPT_SOFT )
+ lc->size = mmdsr.unpack_dd();
+ int norg = mmdsr.unpack_db();
+ if ( norg > 0 )
+ {
+ lc->orgbytes.resize(norg);
+ mmdsr.unpack_obj(lc->orgbytes.begin(), norg);
+ }
+ lc->cmd.ea = mmdsr.unpack_ea64();
+ if ( lc->cmd.ea != BADADDR )
+ mmdsr.unpack_obj(&lc->cmd, sizeof(lc->cmd));
+ }
+ verb(("update_lowcnd(ea=%a cnd=%s)\n", ea, lc->cndbody.c_str()));
+ }
+ int nupdated;
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_update_lowcnds(&nupdated, lowcnds.begin(), nlowcnds, &errbuf);
+ verb((" update_lowcnds => %d\n", drc));
+
+ req.pack_dd(drc);
+ req.pack_dd(nupdated);
+ if ( drc != DRC_OK )
+ req.pack_str(errbuf);
+}
+
+//--------------------------------------------------------------------------
+bool dbg_rpc_handler_t::check_broken_connection(pid_t pid)
+{
+ bool result = false;
+ dispatcher->clients_list->lock();
+ client_handlers_list_t::storage_t::iterator p;
+ for ( p = dispatcher->clients_list->storage.begin();
+ p != dispatcher->clients_list->storage.end();
+ ++p )
+ {
+ dbg_rpc_handler_t *h = (dbg_rpc_handler_t *) p->first;
+ if ( h == this )
+ continue;
+
+ debmod_t *d = h->get_debugger_instance();
+ if ( d->broken_connection && d->pid == pid && d->dbg_continue_broken_connection(pid) )
+ {
+ dbg_mod->dbg_term();
+ delete dbg_mod;
+ dbg_mod = d;
+ result = true;
+ verb(("reusing previously broken debugging session\n"));
+
+#ifndef __SINGLE_THREADED_SERVER__
+ qthread_t thr = p->second;
+
+ // free thread
+ if ( thr != NULL )
+ qthread_free(thr);
+#endif
+
+ h->term_irs();
+ dispatcher->clients_list->storage.erase(p);
+ delete h;
+
+ d->broken_connection = false;
+ break;
+ }
+ }
+ dispatcher->clients_list->unlock();
+ return result;
+}
+
+//-------------------------------------------------------------------------
+int dbg_rpc_handler_t::handle_server_ioctl(
+ int fn,
+ void **out,
+ ssize_t *outsz,
+ memory_deserializer_t &mmdsr)
+{
+ int code = -1;
+ verb(("handle_server_ioctl(fn=%d, bufsize=%" FMT_Z ").\n", fn, mmdsr.size()));
+ return code;
+}
+
+//-------------------------------------------------------------------------
+int dbg_rpc_handler_t::on_recv_packet_progress(bool *performed)
+{
+ *performed = poll_debug_events;
+ return poll_debug_events ? poll_events(TIMEOUT) : 0;
+}
+
+//--------------------------------------------------------------------------
+drc_t dbg_rpc_handler_t::rpc_attach_process(
+ qstring *errbuf,
+ memory_deserializer_t &mmdsr)
+{
+ pid_t pid = mmdsr.unpack_dd();
+ int event_id = mmdsr.unpack_dd();
+ int flags = mmdsr.unpack_dd();
+ drc_t drc = check_broken_connection(pid)
+ ? DRC_OK
+ : dbg_mod->dbg_attach_process(pid, event_id, flags, errbuf);
+ verb(("attach_process(pid=%d, evid=%d) => %d\n", pid, event_id, drc));
+ return drc;
+}
+
+//-------------------------------------------------------------------------
+void dbg_rpc_handler_t::append_start_or_attach(bytevec_t &req, drc_t drc, const qstring &errbuf) const
+{
+ req.pack_dd(drc);
+ if ( drc > DRC_NONE )
+ {
+ debapp_attrs_t attrs;
+ dbg_mod->dbg_get_debapp_attrs(&attrs);
+ append_debapp_attrs(req, attrs);
+ append_dynamic_register_set(req, dbg_mod->idaregs);
+ }
+ else
+ {
+ req.pack_str(errbuf);
+ }
+}
+
+//-------------------------------------------------------------------------
+void dbg_rpc_handler_t::shutdown_gracefully(int /*signum*/)
+{
+ debmod_t *d = get_debugger_instance();
+ if ( d != NULL )
+ d->dbg_exit_process(NULL); // kill the process instead of letting it run in wild
+}
+
+//--------------------------------------------------------------------------
+// performs requests on behalf of a remote client
+// client -> server
+#ifdef __UNIX__
+# define PERM_0755 (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+# define IS_SUBPATH strneq
+#else
+# define PERM_0755 0755
+# define IS_SUBPATH strnieq
+#endif
+bytevec_t dbg_rpc_handler_t::on_send_request_interrupt(const rpc_packet_t *rp)
+{
+ // While the server is performing a request, it should not poll
+ // for debugger events
+ bool saved_poll_mode = poll_debug_events;
+ poll_debug_events = false;
+
+ memory_deserializer_t mmdsr(rp+1, rp->length);
+ bytevec_t req = prepare_rpc_packet(RPC_OK);
+#if defined(__EXCEPTIONS) || defined(__NT__)
+ try
+#endif
+ {
+ switch ( rp->code )
+ {
+ case RPC_INIT:
+ {
+ dbg_mod->debugger_flags = mmdsr.unpack_dd();
+ bool debug_debugger = mmdsr.unpack_dd() != 0;
+ if ( debug_debugger )
+ verbose = true;
+
+ dbg_mod->dbg_set_debugging(debug_debugger);
+ qstring errbuf;
+ uint32_t flags2 = 0;
+ drc_t drc = dbg_mod->dbg_init(&flags2, &errbuf);
+ verb(("init(debug_debugger=%d) => %d (flags2=%d)\n", debug_debugger, drc, flags2));
+ req.pack_dd(drc);
+ req.pack_dd(flags2);
+ if ( drc != DRC_OK )
+ req.pack_str(errbuf);
+ }
+ break;
+
+ case RPC_TERM:
+ // Do not dbg_term() here, as it will be called
+ // at the end of server.cpp's handle_single_session(),
+ // right after this.
+ // dbg_mod->dbg_term();
+ // verb(("term()\n"));
+ break;
+
+ case RPC_GET_PROCESSES:
+ {
+ procinfo_vec_t procs;
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_get_processes(&procs, &errbuf);
+ req.pack_dd(drc);
+ if ( drc == DRC_OK )
+ append_process_info_vec(req, &procs);
+ else
+ req.pack_str(errbuf);
+ verb(("get_processes() => %d\n", drc));
+ }
+ break;
+
+ case RPC_DETACH_PROCESS:
+ {
+ drc_t drc = dbg_mod->dbg_detach_process();
+ req.pack_dd(drc);
+ verb(("detach_process() => %d\n", drc));
+ }
+ break;
+
+ case RPC_START_PROCESS:
+ {
+ const char *path = mmdsr.unpack_str();
+ const char *args = mmdsr.unpack_str();
+ const char *sdir = mmdsr.unpack_str();
+ int flags = mmdsr.unpack_dd();
+ const char *input = mmdsr.unpack_str();
+ uint32 crc32 = mmdsr.unpack_dd();
+ qstring errbuf;
+ drc_t drc = DRC_NOFILE;
+ if ( path != NULL && sdir != NULL && input != NULL ) // protect against malicious ida
+ {
+ drc = dbg_mod->dbg_start_process(path, args, sdir, flags, input, crc32, &errbuf);
+ verb(("start_process(path=%s args=%s flags=%s%s%s\n"
+ " sdir=%s\n"
+ " input=%s crc32=%x) => %d\n",
+ path, args,
+ flags & DBG_PROC_IS_DLL ? " is_dll" : "",
+ flags & DBG_PROC_IS_GUI ? " under_gui" : "",
+ flags & DBG_HIDE_WINDOW ? " hide_window" : "",
+ sdir,
+ input, crc32,
+ drc));
+ }
+ append_start_or_attach(req, drc, errbuf);
+ }
+ break;
+
+ case RPC_GET_DEBUG_EVENT:
+ {
+ int timeout_ms = mmdsr.unpack_dd();
+ gdecode_t result = GDE_NO_EVENT;
+ if ( !has_pending_event )
+ result = dbg_mod->dbg_get_debug_event(&ev, timeout_ms);
+ req.pack_dd(result);
+ if ( result >= GDE_ONE_EVENT )
+ {
+ append_debug_event(req, &ev);
+ verb(("got event: %s\n", debug_event_str(&ev)));
+ }
+ else if ( !has_pending_event )
+ {
+ saved_poll_mode = true;
+ }
+ verbev(("get_debug_event(timeout=%d) => %d (has_pending=%d, willpoll=%d)\n", timeout_ms, result, has_pending_event, saved_poll_mode));
+ }
+ break;
+
+ case RPC_ATTACH_PROCESS:
+ {
+ qstring errbuf;
+ append_start_or_attach(req, rpc_attach_process(&errbuf, mmdsr), errbuf);
+ }
+ break;
+
+ case RPC_PREPARE_TO_PAUSE_PROCESS:
+ {
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_prepare_to_pause_process(&errbuf);
+ verb(("prepare_to_pause_process() => %d\n", drc));
+ req.pack_dd(drc);
+ if ( drc < DRC_NONE )
+ req.pack_str(errbuf);
+ }
+ break;
+
+ case RPC_EXIT_PROCESS:
+ {
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_exit_process(&errbuf);
+ verb(("exit_process() => %d\n", drc));
+ req.pack_dd(drc);
+ if ( drc < DRC_NONE )
+ req.pack_str(errbuf);
+ }
+ break;
+
+ case RPC_CONTINUE_AFTER_EVENT:
+ {
+ extract_debug_event(&ev, mmdsr);
+ drc_t drc = dbg_mod->dbg_continue_after_event(&ev);
+ verb(("continue_after_event(...) => %d\n", drc));
+ req.pack_dd(drc);
+ }
+ break;
+
+ case RPC_STOPPED_AT_DEBUG_EVENT:
+ {
+ bool dlls_added = mmdsr.unpack_db() != 0;
+ bool ask_thr_names = mmdsr.unpack_db() != 0;
+ import_infos_t infos;
+ thread_name_vec_t thr_names;
+ dbg_mod->dbg_stopped_at_debug_event(&infos, dlls_added, ask_thr_names ? &thr_names : NULL);
+ process_import_requests(infos);
+ name_info_t *ni = dbg_mod->get_debug_names();
+ int err = RPC_OK;
+ if ( ni != NULL )
+ {
+ err = send_debug_names_to_ida(ni->addrs.begin(), ni->names.begin(), (int)ni->addrs.size());
+ dbg_mod->clear_debug_names();
+ }
+ if ( ask_thr_names )
+ {
+ uint32 n = thr_names.size();
+ req.pack_dd(n);
+ for ( int i=0; i < n; ++i )
+ {
+ thread_name_t &tn = thr_names[i];
+ req.pack_dd(tn.tid);
+ req.pack_str(tn.name);
+ }
+ }
+ }
+ break;
+
+ case RPC_TH_SUSPEND:
+ {
+ thid_t tid = mmdsr.unpack_dd();
+ drc_t drc = dbg_mod->dbg_thread_suspend(tid);
+ verb(("thread_suspend(tid=%d) => %d\n", tid, drc));
+ req.pack_dd(drc);
+ }
+ break;
+
+ case RPC_TH_CONTINUE:
+ {
+ thid_t tid = mmdsr.unpack_dd();
+ drc_t drc = dbg_mod->dbg_thread_continue(tid);
+ verb(("thread_continue(tid=%d) => %d\n", tid, drc));
+ req.pack_dd(drc);
+ }
+ break;
+
+ case RPC_SET_RESUME_MODE:
+ {
+ thid_t tid = mmdsr.unpack_dd();
+ resume_mode_t resmod = resume_mode_t(mmdsr.unpack_dd());
+ drc_t drc = dbg_mod->dbg_set_resume_mode(tid, resmod);
+ verb(("set_resume_mode(tid=%d, resmod=%d) => %d\n", tid, resmod, drc));
+ req.pack_dd(drc);
+ }
+ break;
+
+ case RPC_READ_REGS:
+ {
+ drc_t drc = DRC_NONE;
+ qstring errbuf;
+ bytevec_t regmap;
+ regvals_t values;
+ thid_t tid = mmdsr.unpack_dd();
+ int clsmask = mmdsr.unpack_dd();
+ int nregs = mmdsr.unpack_dd();
+ if ( nregs <= 0 || nregs > dbg_mod->nregs )
+ {
+ errbuf.sprnt("read_regs(tid=%d, mask=%x, nregs=%d) => 0 "
+ "(incorrect nregs, should be in range 0..%d)\n",
+ tid, clsmask, nregs, dbg_mod->nregs);
+ }
+ else
+ {
+ regmap.resize((nregs+7)/8);
+ mmdsr.unpack_obj(regmap.begin(), regmap.size());
+ values.resize(dbg_mod->nregs);
+ drc = dbg_mod->dbg_read_registers(tid, clsmask, values.begin(), &errbuf);
+ verb(("read_regs(tid=%d, mask=%x) => %d\n", tid, clsmask, drc));
+ }
+ req.pack_dd(drc);
+ if ( drc == DRC_OK )
+ append_regvals(req, values.begin(), nregs, regmap.begin());
+ else
+ req.pack_str(errbuf);
+ }
+ break;
+
+ case RPC_WRITE_REG:
+ {
+ thid_t tid = mmdsr.unpack_dd();
+ int reg_idx = mmdsr.unpack_dd();
+ regval_t value;
+ unpack_regvals(&value, 1, NULL, mmdsr);
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_write_register(tid, reg_idx, &value, &errbuf);
+ verb(("write_reg(tid=%d) => %d\n", tid, drc));
+ req.pack_dd(drc);
+ if ( drc < DRC_NONE )
+ req.pack_str(errbuf);
+ }
+ break;
+
+ case RPC_GET_SREG_BASE:
+ {
+ thid_t tid = mmdsr.unpack_dd();
+ int sreg_value = mmdsr.unpack_dd();
+ ea_t ea;
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_thread_get_sreg_base(&ea, tid, sreg_value, &errbuf);
+ verb(("get_thread_sreg_base(tid=%d, %d) => %a\n", tid, sreg_value, drc == DRC_OK ? ea : BADADDR));
+ req.pack_dd(drc);
+ if ( drc == DRC_OK )
+ req.pack_ea64(ea);
+ else
+ req.pack_str(errbuf);
+ }
+ break;
+
+ case RPC_SET_EXCEPTION_INFO:
+ {
+ int qty = mmdsr.unpack_dd();
+ exception_info_t *extable = extract_exception_info(qty, mmdsr);
+ dbg_mod->dbg_set_exception_info(extable, qty);
+ delete [] extable;
+ verb(("set_exception_info(qty=%d)\n", qty));
+ }
+ break;
+
+ case RPC_GET_MEMORY_INFO:
+ {
+ meminfo_vec_t areas;
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_get_memory_info(areas, &errbuf);
+ int qty = areas.size();
+ verb(("get_memory_info() => %d (qty=%d)\n", drc, qty));
+ req.pack_dd(drc + (-DRC_IDBSEG));
+ if ( drc == DRC_OK )
+ {
+ req.pack_dd(qty);
+ for ( int i=0; i < qty; i++ )
+ append_memory_info(req, &areas[i]);
+ }
+ else
+ {
+ req.pack_str(errbuf);
+ }
+ }
+ break;
+
+ case RPC_GET_SCATTERED_IMAGE:
+ {
+ ea_t base = mmdsr.unpack_ea64();
+ scattered_image_t si;
+ int result = dbg_mod->dbg_get_scattered_image(si, base);
+ int qty = si.size();
+ verb(("get_scattered_image(base=%a) => %d (qty=%d)\n", base, result, qty));
+ req.pack_dd(result+2);
+ if ( result > 0 )
+ {
+ req.pack_dd(qty);
+ for ( int i=0; i < qty; i++ )
+ append_scattered_segm(req, &si[i]);
+ }
+ }
+ break;
+
+ case RPC_GET_IMAGE_UUID:
+ {
+ ea_t base = mmdsr.unpack_ea64();
+ bytevec_t uuid;
+ bool result = dbg_mod->dbg_get_image_uuid(&uuid, base);
+ int size = uuid.size();
+ verb(("get_image_uuid(base=%a) => %d (size=%d)\n", base, result, size));
+ req.pack_dd(result);
+ if ( result )
+ req.pack_buf(uuid.begin(), size);
+ }
+ break;
+
+ case RPC_GET_SEGM_START:
+ {
+ ea_t base = mmdsr.unpack_ea64();
+ const char *segname = mmdsr.unpack_str();
+ ea_t result = dbg_mod->dbg_get_segm_start(base, segname);
+ verb(("get_segm_start(base=%a, segname=%s) => %a\n", base, segname, result));
+ req.pack_ea64(result);
+ }
+ break;
+
+ case RPC_READ_MEMORY:
+ {
+ ea_t ea = mmdsr.unpack_ea64();
+ size_t size = mmdsr.unpack_dd();
+ uchar *buf = new uchar[size];
+ qstring errbuf;
+ ssize_t result = dbg_mod->dbg_read_memory(ea, buf, size, &errbuf);
+ verb(("read_memory(ea=%a size=%" FMT_Z ") => %" FMT_ZS, ea, size, result));
+ if ( result > 0 && size == 1 )
+ verb((" (0x%02X)\n", *buf));
+ else
+ verb(("\n"));
+ req.pack_dd(uint32(result));
+ if ( result > 0 )
+ req.append(buf, result);
+ else
+ req.pack_str(errbuf);
+ delete[] buf;
+ }
+ break;
+
+ case RPC_WRITE_MEMORY:
+ {
+ ea_t ea = mmdsr.unpack_ea64();
+ size_t size = mmdsr.unpack_dd();
+ uchar *buf = new uchar[size];
+ mmdsr.unpack_obj(buf, size);
+ qstring errbuf;
+ ssize_t result = dbg_mod->dbg_write_memory(ea, buf, size, &errbuf);
+ verb(("write_memory(ea=%a size=%" FMT_Z ") => %" FMT_ZS, ea, size, result));
+ if ( result && size == 1 )
+ verb((" (0x%02X)\n", *buf));
+ else
+ verb(("\n"));
+ req.pack_dd(uint32(result));
+ if ( result <= 0 )
+ req.pack_str(errbuf);
+ delete[] buf;
+ }
+ break;
+
+ case RPC_ISOK_BPT:
+ {
+ bpttype_t type = mmdsr.unpack_dd();
+ ea_t ea = mmdsr.unpack_ea64();
+ int len = mmdsr.unpack_dd() - 1;
+ int result = dbg_mod->dbg_is_ok_bpt(type, ea, len);
+ verb(("isok_bpt(type=%d ea=%a len=%d) => %d\n", type, ea, len, result));
+ req.pack_dd(result);
+ }
+ break;
+
+ case RPC_UPDATE_BPTS:
+ {
+ int ret = rpc_update_bpts(req, mmdsr);
+ if ( ret == 0 )
+ verb(("rpc_update_bpts failed!\n"));
+ }
+ break;
+
+ case RPC_UPDATE_LOWCNDS:
+ rpc_update_lowcnds(req, mmdsr);
+ break;
+
+ case RPC_EVAL_LOWCND:
+ {
+ thid_t tid = mmdsr.unpack_dd();
+ ea_t ea = mmdsr.unpack_ea64();
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_eval_lowcnd(tid, ea, &errbuf);
+ req.pack_dd(drc);
+ if ( drc != DRC_OK )
+ req.pack_str(errbuf);
+ verb(("eval_lowcnd(tid=%d, ea=%a) => %d\n", tid, ea, drc));
+ }
+ break;
+
+ case RPC_OPEN_FILE:
+ {
+ const char *path = mmdsr.unpack_str();
+ bool readonly = mmdsr.unpack_dd() != 0;
+ int64 fsize = 0;
+ int fn = find_free_channel();
+ if ( fn != -1 )
+ {
+ if ( readonly )
+ {
+ channels[fn] = fopenRB(path);
+ }
+ else
+ {
+ char dir[QMAXPATH];
+ if ( qdirname(dir, sizeof(dir), path) && !qisdir(dir) )
+ {
+ char absdir[QMAXPATH];
+ qmake_full_path(absdir, sizeof(absdir), dir);
+ char cwd[QMAXPATH];
+ qgetcwd(cwd, sizeof(cwd));
+ if ( IS_SUBPATH(absdir, cwd, qstrlen(cwd)) )
+ {
+ qstrvec_t subpaths;
+ while ( !qisdir(absdir) )
+ {
+ subpaths.insert(subpaths.begin(), absdir);
+ if ( !qdirname(absdir, sizeof(absdir), absdir) )
+ break;
+ }
+ for ( size_t i = 0, n = subpaths.size(); i < n; ++i )
+ {
+ const char *subdir = subpaths[i].c_str();
+ verb(("open_file() creating directory %s\n", subdir));
+ if ( qmkdir(subdir, PERM_0755) != 0 )
+ break;
+ }
+ }
+ }
+ channels[fn] = fopenWB(path);
+ }
+ if ( channels[fn] == NULL )
+ fn = -1;
+ else if ( readonly )
+ fsize = qfsize(channels[fn]);
+ }
+ verb(("open_file('%s', %d) => %d %" FMT_64 "d\n", path, readonly, fn, fsize));
+ req.pack_dd(fn);
+ if ( fn != -1 )
+ req.pack_dq(fsize);
+ else
+ req.pack_dd(qerrcode());
+ }
+ break;
+
+ case RPC_CLOSE_FILE:
+ {
+ int fn = mmdsr.unpack_dd();
+ if ( fn >= 0 && fn < qnumber(channels) )
+ {
+ FILE *fp = channels[fn];
+ if ( fp != NULL )
+ {
+#ifdef __UNIX__
+ fchmod(fileno(fp), PERM_0755); // set mode 0755 for unix applications
+#endif
+ qfclose(fp);
+ channels[fn] = NULL;
+ }
+ }
+ verb(("close_file(%d)\n", fn));
+ }
+ break;
+
+ case RPC_READ_FILE:
+ {
+ char *buf = NULL;
+ int fn = mmdsr.unpack_dd();
+ int64 off = mmdsr.unpack_dq();
+ int32 size = mmdsr.unpack_dd();
+ int32 s2 = 0;
+ if ( size > 0 )
+ {
+ buf = new char[size];
+ qfseek(channels[fn], off, SEEK_SET);
+ s2 = qfread(channels[fn], buf, size);
+ }
+ req.pack_dd(s2);
+ if ( size != s2 )
+ req.pack_dd(qerrcode());
+ else
+ req.append(buf, s2);
+ delete[] buf;
+ verb(("read_file(%d, 0x%" FMT_64 "X, %d) => %d\n", fn, off, size, s2));
+ }
+ break;
+
+ case RPC_WRITE_FILE:
+ {
+ char *buf = NULL;
+ int fn = mmdsr.unpack_dd();
+ uint64 off = mmdsr.unpack_dq();
+ uint32 size = mmdsr.unpack_dd();
+ if ( size > 0 )
+ {
+ buf = new char[size];
+ mmdsr.unpack_obj(buf, size);
+ }
+ qfseek(channels[fn], off, SEEK_SET);
+ uint32 s2 = buf == NULL ? 0 : qfwrite(channels[fn], buf, size);
+ req.pack_dd(size);
+ if ( size != s2 )
+ req.pack_dd(qerrcode());
+ delete [] buf;
+ verb(("write_file(%d, 0x%" FMT_64 "X, %u) => %u\n", fn, off, size, s2));
+ }
+ break;
+
+ case RPC_EVOK:
+ req.clear();
+ verbev(("got evok!\n"));
+ break;
+
+ case RPC_IOCTL:
+ {
+ int code = handle_ioctl_packet(req, mmdsr.ptr, mmdsr.end);
+ if ( code != RPC_OK )
+ req = prepare_rpc_packet((uchar)code);
+ }
+ break;
+
+ case RPC_UPDATE_CALL_STACK:
+ {
+ call_stack_t trace;
+ thid_t tid = mmdsr.unpack_dd();
+ drc_t drc = dbg_mod->dbg_update_call_stack(tid, &trace);
+ req.pack_dd(drc);
+ if ( drc == DRC_OK )
+ append_call_stack(req, trace);
+ }
+ break;
+
+ case RPC_APPCALL:
+ {
+ ea_t func_ea = mmdsr.unpack_ea64();
+ thid_t tid = mmdsr.unpack_dd();
+ int stkarg_nbytes = mmdsr.unpack_dd();
+ int flags = mmdsr.unpack_dd();
+
+ regobjs_t regargs, retregs;
+ relobj_t stkargs;
+ regobjs_t *rr = (flags & APPCALL_MANUAL) == 0 ? &retregs : NULL;
+ extract_appcall(®args, &stkargs, rr, mmdsr);
+
+ qstring errbuf;
+ debug_event_t event;
+ ea_t sp = dbg_mod->dbg_appcall(func_ea, tid, stkarg_nbytes, ®args, &stkargs,
+ &retregs, &errbuf, &event, flags);
+ req.pack_ea64(sp);
+ if ( sp == BADADDR )
+ {
+ if ( (flags & APPCALL_DEBEV) != 0 )
+ append_debug_event(req, &event);
+ req.pack_str(errbuf);
+ }
+ else if ( (flags & APPCALL_MANUAL) == 0 )
+ {
+ append_regobjs(req, retregs, true);
+ }
+ }
+ break;
+
+ case RPC_CLEANUP_APPCALL:
+ {
+ thid_t tid = mmdsr.unpack_dd();
+ drc_t drc = dbg_mod->dbg_cleanup_appcall(tid);
+ req.pack_dd(drc);
+ }
+ break;
+
+ case RPC_REXEC:
+ {
+ const char *cmdline = mmdsr.unpack_str();
+ int code = dbg_mod->dbg_rexec(cmdline);
+ req.pack_dd(code);
+ }
+ break;
+
+ case RPC_BIN_SEARCH:
+ {
+ ea_t start_ea = mmdsr.unpack_ea64();
+ ea_t end_ea = mmdsr.unpack_ea64();
+ int cnt = mmdsr.unpack_dd();
+ compiled_binpat_vec_t ptns;
+ ptns.resize(cnt);
+ for ( int i=0; i < cnt; ++i )
+ {
+ compiled_binpat_t &p = ptns[i];
+ // bytes
+ int sz = mmdsr.unpack_dd();
+ if ( sz != 0 )
+ {
+ p.bytes.resize(sz);
+ mmdsr.unpack_obj(p.bytes.begin(), sz);
+ }
+ // mask
+ sz = mmdsr.unpack_dd();
+ if ( sz != 0 )
+ {
+ p.mask.resize(sz);
+ mmdsr.unpack_obj(p.mask.begin(), sz);
+ }
+ // strlits
+ sz = mmdsr.unpack_dd();
+ p.strlits.resize(sz);
+ for ( int j=0; j < sz; ++j )
+ {
+ p.strlits[j].start_ea = mmdsr.unpack_ea64();
+ p.strlits[j].end_ea = mmdsr.unpack_ea64();
+ }
+ // encidx
+ p.encidx = mmdsr.unpack_dd();
+ }
+ int srch_flags = mmdsr.unpack_dd();
+ ea_t srch_ea;
+ qstring errbuf;
+ drc_t drc = dbg_mod->dbg_bin_search(&srch_ea, start_ea, end_ea, ptns, srch_flags, &errbuf);
+ req.pack_dd(drc);
+ if ( drc == DRC_OK )
+ req.pack_ea64(srch_ea);
+ else if ( drc != DRC_FAILED ) // DRC_FAILED means not found
+ req.pack_str(errbuf);
+ }
+ break;
+
+ default:
+ req = prepare_rpc_packet(RPC_UNK);
+ break;
+ }
+ }
+#if defined(__EXCEPTIONS) || defined(__NT__)
+ catch ( const std::bad_alloc & )
+ {
+ req = prepare_rpc_packet(RPC_MEM);
+ }
+#endif
+
+ if ( saved_poll_mode )
+ poll_debug_events = true;
+ return req;
+}
+
+//--------------------------------------------------------------------------
+// poll for events from the debugger module
+int dbg_rpc_handler_t::poll_events(int timeout_ms)
+{
+ int code = 0;
+ if ( !has_pending_event )
+ {
+ // immediately set poll_debug_events to false to avoid recursive calls.
+ poll_debug_events = false;
+ has_pending_event = dbg_mod->dbg_get_debug_event(&pending_event, timeout_ms) >= GDE_ONE_EVENT;
+ if ( has_pending_event )
+ {
+ verbev(("got event, sending it, poll will be 0 now\n"));
+ bytevec_t req = prepare_rpc_packet(RPC_EVENT);
+ append_debug_event(req, &pending_event);
+ code = send_data(req);
+ has_pending_event = false;
+ }
+ else
+ { // no event, continue to poll
+ poll_debug_events = true;
+ }
+ }
+ return code;
+}
+
+//--------------------------------------------------------------------------
+// this function runs on the server side
+// a dbg_rpc_client sends an RPC_SYNC request and the server must give the stub to the client
+bool dbg_rpc_handler_t::rpc_sync_stub(const char *server_stub_name, const char *ida_stub_name)
+{
+ bool ok = false;
+ int32 crc32 = -1;
+ linput_t *li = open_linput(server_stub_name, false);
+ if ( li != NULL )
+ {
+ crc32 = calc_file_crc32(li);
+ close_linput(li);
+ }
+
+ bytevec_t stub = prepare_rpc_packet(RPC_SYNC_STUB);
+ stub.pack_str(ida_stub_name);
+ stub.pack_dd(crc32);
+ rpc_packet_t *rp = send_request_and_receive_reply(stub);
+
+ if ( rp == NULL )
+ return ok;
+
+ const uchar *answer = (uchar *)(rp+1);
+ const uchar *end = answer + rp->length;
+ size_t size = unpack_dd(&answer, end);
+ if ( size == 1 )
+ {
+ ok = true;
+ }
+ else if ( size != 0 )
+ {
+ FILE *fp = fopenWB(server_stub_name);
+ if ( fp != NULL )
+ {
+ ok = qfwrite(fp, answer, size) == size;
+ dmsg("Updated kernel debugger stub: %s\n", ok ? "success" : "failed");
+ qfclose(fp);
+ }
+ else
+ {
+ dwarning("Could not update the kernel debugger stub.\n%s", qerrstr());
+ }
+ }
+ qfree(rp);
+
+ return ok;
+}
+
+//--------------------------------------------------------------------------
+//lint -e{818} 'addrs' could be declared as pointing to const
+int dbg_rpc_handler_t::send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty)
+{
+ if ( qty == 0 )
+ return RPC_OK;
+
+ bytevec_t buf;
+
+ const size_t SZPACKET = 1300; // try not to go over the usual network MSS
+ // (this number is slightly less that 1460 because
+ // we stop the loop going over this number)
+ while ( qty > 0 )
+ {
+ buf.qclear();
+
+ ea_t old = 0;
+ const char *optr = "";
+
+ // Start appending names and EAs
+ int i = 0;
+ while ( i < qty )
+ {
+ adiff_t diff = *addrs - old;
+ bool neg = diff < 0;
+ if ( neg )
+ diff = -diff;
+
+ buf.pack_ea64(diff); // send address deltas
+ buf.pack_dd(neg);
+
+ old = *addrs;
+ const char *nptr = *names;
+ int len = 0;
+
+ // do not send repeating prefixes of names
+ while ( nptr[len] != '\0' && nptr[len] == optr[len] ) //lint !e690 wrong access
+ len++;
+
+ buf.pack_dd(len);
+ buf.pack_str(nptr+len);
+ optr = nptr;
+ addrs++;
+ names++;
+ i++;
+
+ if ( buf.size() > SZPACKET )
+ break;
+ }
+ qty -= i;
+
+ bytevec_t req = prepare_rpc_packet(RPC_SET_DEBUG_NAMES);
+ req.pack_dd(i);
+ req.append(buf.begin(), buf.size());
+
+ // should return a qty as much as sent...if not probably network error!
+ if ( i != send_request_get_long_result(req) )
+ return RPC_UNK;
+ }
+
+ return RPC_OK;
+}
+
+//--------------------------------------------------------------------------
+int dbg_rpc_handler_t::send_debug_event_to_ida(const debug_event_t *debev, int rqflags)
+{
+ bytevec_t req = prepare_rpc_packet(RPC_HANDLE_DEBUG_EVENT);
+ append_debug_event(req, debev);
+ req.pack_dd(rqflags);
+ return send_request_get_long_result(req);
+}
+
+//--------------------------------------------------------------------------
+void dbg_rpc_handler_t::process_import_requests(const import_infos_t &infos)
+{
+ // in an effort to avoid sending large amounts of symbol data over the network,
+ // we attempt to import symbols for each dll on the client side.
+ // if the client does not support such behavior, then we simply parse the symbols
+ // on the server side and append to the list of debug names to send to IDA, as normal.
+ for ( import_infos_t::const_iterator i = infos.begin(); i != infos.end(); ++i )
+ {
+ ea_t base = i->base;
+ const char *path = i->path.c_str();
+ const bytevec_t &uuid = i->uuid;
+
+ bytevec_t req = prepare_rpc_packet(RPC_IMPORT_DLL);
+ req.pack_ea64(base);
+ req.pack_str(path);
+ req.pack_buf(uuid.begin(), uuid.size());
+
+ int code = send_request_get_long_result(req);
+ if ( code < 0 ) // cancelled or network error
+ return;
+ if ( code != 0 ) // request failed, fall back to parsing symbols server-side
+ dbg_mod->import_dll(*i);
+ }
+}
+
+//--------------------------------------------------------------------------
+bool dbg_rpc_handler_t::get_broken_connection(void)
+{
+ return get_debugger_instance()->broken_connection;
+}
+
+//--------------------------------------------------------------------------
+void dbg_rpc_handler_t::set_broken_connection(void)
+{
+ get_debugger_instance()->broken_connection = true;
+}
+
+//-------------------------------------------------------------------------
+int dbg_rpc_handler_t::kill_process(void)
+{
+ const int NSEC = 5;
+ dbg_mod->dbg_exit_process(NULL);
+
+ // now, wait up to NSEC seconds until the process is gone
+ qtime64_t wait_start = qtime64();
+ qtime64_t wait_threshold = make_qtime64(
+ get_secs(wait_start) + NSEC,
+ get_usecs(wait_start));
+ while ( qtime64() < wait_threshold )
+ {
+ gdecode_t result = dbg_mod->dbg_get_debug_event(&ev, 100);
+ if ( result >= GDE_ONE_EVENT )
+ {
+ dbg_mod->dbg_continue_after_event(&ev);
+ if ( ev.eid() == PROCESS_EXITED )
+ return 0;
+ }
+ }
+ return NSEC;
+}
+
+//--------------------------------------------------------------------------
+int debmod_t::send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty)
+{
+ dbg_rpc_handler_t *s = (dbg_rpc_handler_t *)rpc;
+ return s->send_debug_names_to_ida(addrs, names, qty);
+}
+
+//--------------------------------------------------------------------------
+int debmod_t::send_debug_event_to_ida(const debug_event_t *ev, int rqflags)
+{
+ dbg_rpc_handler_t *s = (dbg_rpc_handler_t *)rpc;
+ return s->send_debug_event_to_ida(ev, rqflags);
+}
diff --git a/idasdk75/dbg/dbg_rpc_handler.h b/idasdk75/dbg/dbg_rpc_handler.h
new file mode 100644
index 0000000..b8a1e0a
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_handler.h
@@ -0,0 +1,65 @@
+#ifndef __DBG_RPC_HANDLER__
+#define __DBG_RPC_HANDLER__
+
+#include
+
+#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__
diff --git a/idasdk75/dbg/dbg_rpc_handler_ioctls.h b/idasdk75/dbg/dbg_rpc_handler_ioctls.h
new file mode 100644
index 0000000..ae00c24
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_handler_ioctls.h
@@ -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__
diff --git a/idasdk75/dbg/dbg_rpc_hlp.cpp b/idasdk75/dbg/dbg_rpc_hlp.cpp
new file mode 100644
index 0000000..d59355d
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_hlp.cpp
@@ -0,0 +1,367 @@
+
+#include
+#include
+
+#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);
+}
diff --git a/idasdk75/dbg/dbg_rpc_hlp.h b/idasdk75/dbg/dbg_rpc_hlp.h
new file mode 100644
index 0000000..8b32efb
--- /dev/null
+++ b/idasdk75/dbg/dbg_rpc_hlp.h
@@ -0,0 +1,80 @@
+#ifndef __DBG_RPC_HLP__
+#define __DBG_RPC_HLP__
+
+#include
+#include
+#include
+#include
+
+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__
diff --git a/idasdk75/dbg/deb_arm.hpp b/idasdk75/dbg/deb_arm.hpp
new file mode 100644
index 0000000..44f8643
--- /dev/null
+++ b/idasdk75/dbg/deb_arm.hpp
@@ -0,0 +1,19 @@
+#ifndef __DEB_ARM__
+#define __DEB_ARM__
+
+#include
+#include
+
+#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.
+
+int is_arm_valid_bpt(bpttype_t type, ea_t ea, int len);
+
+#endif
+
diff --git a/idasdk75/dbg/deb_pc.hpp b/idasdk75/dbg/deb_pc.hpp
new file mode 100644
index 0000000..9f65bff
--- /dev/null
+++ b/idasdk75/dbg/deb_pc.hpp
@@ -0,0 +1,47 @@
+#ifndef __DEB_PC__
+#define __DEB_PC__
+
+#include
+#include
+#include
+
+#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
diff --git a/idasdk75/dbg/debmod.cpp b/idasdk75/dbg/debmod.cpp
new file mode 100644
index 0000000..37f5f48
--- /dev/null
+++ b/idasdk75/dbg/debmod.cpp
@@ -0,0 +1,1329 @@
+/*
+ Base debmod class.
+*/
+
+#ifdef __NT__
+# include
+#else
+# include
+#endif
+
+#include "debmod.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+//--------------------------------------------------------------------------
+// Variables to be used by built-in IDC functions
+debmod_t *idc_debmod;
+thid_t idc_thread;
+
+//--------------------------------------------------------------------------
+// Initialize static members
+bool debmod_t::reuse_broken_connections = false;
+
+//--------------------------------------------------------------------------
+static const char *get_event_name(event_id_t id)
+{
+ switch ( id )
+ {
+ case NO_EVENT: return "NO_EVENT";
+ case THREAD_STARTED: return "THREAD_STARTED";
+ case STEP: return "STEP";
+ case PROCESS_DETACHED: return "PROCESS_DETACHED";
+ case PROCESS_STARTED: return "PROCESS_STARTED";
+ case PROCESS_ATTACHED: return "PROCESS_ATTACHED";
+ case PROCESS_SUSPENDED: return "PROCESS_SUSPENDED";
+ case LIB_LOADED: return "LIB_LOADED";
+ case PROCESS_EXITED: return "PROCESS_EXITED";
+ case THREAD_EXITED: return "THREAD_EXITED";
+ case BREAKPOINT: return "BREAKPOINT";
+ case EXCEPTION: return "EXCEPTION";
+ case LIB_UNLOADED: return "LIB_UNLOADED";
+ case INFORMATION: return "INFORMATION";
+ case TRACE_FULL: return "TRACE_FULL";
+ default: return "???";
+ }
+}
+
+#if defined(__MAC__) || defined(__LINUX__) || defined(__ANDROID__)
+//---------------------------------------------------------------------------
+inline bool is_esxi()
+{
+#ifdef __LINUX__
+ static int ok = -1;
+ if ( ok == -1 )
+ {
+ utsname utsinfo;
+ ok = uname(&utsinfo) == 0 && strieq(utsinfo.sysname, "VMkernel");
+ }
+ return ok > 0;
+#else
+ return false;
+#endif
+}
+
+//---------------------------------------------------------------------------
+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*/)
+{
+ // prepare full path if the input_path is relative
+ char full_input[QMAXPATH];
+ if ( startdir[0] != '\0' && !qisabspath(input_path) )
+ {
+ qmake_full_path(full_input, sizeof(full_input), input_path);
+ input_path = full_input;
+ }
+
+ // input file specified in the database does not exist
+ if ( input_path[0] != '\0' && !qfileexist(input_path) )
+ {
+ debmod->dwarning("AUTOHIDE NONE\nInput file is missing: %s", input_path);
+ return DRC_NOFILE;
+ }
+
+ // temporary thing, later we will retrieve the real file name
+ // based on the process id
+ debmod->input_file_path = input_path;
+ debmod->is_dll = (flags & DBG_PROC_IS_DLL) != 0;
+
+ if ( !qfileexist(path) )
+ {
+ debmod->dmsg("%s: %s\n", path, winerr(errno));
+ return DRC_NETERR;
+ }
+
+ drc_t mismatch = DRC_OK;
+ if ( !debmod->check_input_file_crc32(input_file_crc32) )
+ mismatch = DRC_CRC;
+
+ //-V:dbg_srv_64:547 is always true
+#ifdef __EA64__
+ bool dbg_srv_64 = true;
+#else
+ bool dbg_srv_64 = false;
+ if ( (flags & DBG_PROC_64BIT) != 0 )
+ {
+ debmod->dwarning("Cannot debug a 64bit process with the 32bit debugger server, sorry\n");
+ return DRC_NETERR;
+ }
+#endif
+
+ launch_process_params_t lpi;
+ lpi.path = path;
+ lpi.args = args;
+ lpi.startdir = startdir[0] != '\0' ? startdir : NULL;
+ lpi.flags = LP_NO_ASLR | LP_DETACH_TTY;
+
+ // Vmware ESXi sends SIGHUP when we try to detact. It could be because of a
+ // racing condition in but I do not have ESXi handy to check.
+ if ( is_esxi() )
+ lpi.flags &= ~LP_DETACH_TTY;
+ if ( (flags & DBG_NO_TRACE) == 0 )
+ lpi.flags |= LP_TRACE;
+ if ( (flags & DBG_HIDE_WINDOW) != 0 )
+ lpi.flags |= LP_HIDE_WINDOW;
+
+ if ( (flags & DBG_PROC_64BIT) != 0 )
+ {
+ lpi.flags |= LP_LAUNCH_64_BIT;
+ }
+ else if ( (flags & DBG_PROC_32BIT) != 0 )
+ {
+ lpi.flags |= LP_LAUNCH_32_BIT;
+ }
+ else
+ {
+ lpi.flags |= dbg_srv_64 ? LP_LAUNCH_64_BIT : LP_LAUNCH_32_BIT;
+ debmod->dmsg("Launching as %sbit process\n", dbg_srv_64 ? "64" : "32");
+ }
+
+ qstring errbuf;
+ *child_pid = launch_process(lpi, &errbuf);
+
+ if ( *child_pid == NULL )
+ {
+ debmod->dmsg("launch_process: %s", errbuf.c_str());
+ return DRC_NETERR;
+ }
+ return mismatch;
+}
+#endif
+
+//--------------------------------------------------------------------------
+debmod_t::debmod_t(void):
+// ph(PH),
+ debugger_flags(0),
+ rpc(NULL),
+ debug_debugger(false),
+ is_dll(false),
+ sp_idx(-1),
+ pc_idx(-1),
+ fp_idx(-1),
+ nregs(0),
+ broken_connection(false),
+ pid(-1)
+{
+ debapp_attrs.platform = "UNDEFINED";
+ proclist.clear();
+}
+
+//--------------------------------------------------------------------------
+bool debmod_t::same_as_oldmemcfg(const meminfo_vec_t &ranges) const
+{
+ return old_ranges == ranges;
+}
+
+//--------------------------------------------------------------------------
+void debmod_t::save_oldmemcfg(const meminfo_vec_t &ranges)
+{
+ old_ranges = ranges;
+}
+
+//--------------------------------------------------------------------------
+bool debmod_t::check_input_file_crc32(uint32 orig_crc)
+{
+ // take this opportunity to check that the derived class initialized
+ // register related fields correctly
+ QASSERT(30016, sp_idx != -1 && pc_idx != -1 && nregs > 0);
+
+ if ( orig_crc == 0 )
+ return true; // the database has no crc
+
+ linput_t *li = open_linput(input_file_path.c_str(), false);
+ if ( li == NULL )
+ return false;
+
+ uint32 crc = calc_file_crc32(li);
+ close_linput(li);
+ return crc == orig_crc;
+}
+
+//--------------------------------------------------------------------------
+const exception_info_t *debmod_t::find_exception(int code)
+{
+ for ( const exception_info_t *ei = exceptions.begin();
+ ei != exceptions.end();
+ ++ei )
+ {
+ if ( ei->code == (uint)code )
+ return ei;
+ }
+ return NULL;
+}
+
+//--------------------------------------------------------------------------
+bool debmod_t::get_exception_name(int code, char *buf, size_t bufsize)
+{
+ const exception_info_t *ei = find_exception(code);
+ if ( ei != NULL )
+ {
+ qstrncpy(buf, ei->name.c_str(), bufsize);
+ return true;
+ }
+ qsnprintf(buf, bufsize, "%08X", code);
+ return false;
+}
+
+//----------------------------------------------------------------------
+int idaapi debmod_t::get_system_specific_errno(void) const
+{
+ // this code must be acceptable by winerr()
+#ifdef __NT__
+ return GetLastError();
+#else
+ return errno;
+#endif
+}
+
+//-------------------------------------------------------------------------
+ssize_t debmod_t::dvnotif(int code, rpc_engine_t *rpc, const char *format, va_list va)
+{
+ if ( rpc == NULL || rpc->is_client )
+ return dvnotif_client(code, format, va);
+ else
+ return dvnotif_rpc(code, rpc, format, va);
+}
+
+//----------------------------------------------------------------------
+// Display a system error message. This function always returns false
+bool debmod_t::deberr(const char *format, ...)
+{
+ if ( !debug_debugger )
+ return false;
+
+ int code = get_system_specific_errno();
+ va_list va;
+ va_start(va, format);
+ dvnotif(0, rpc, format, va);
+ va_end(va);
+ dmsg(": %s\n", winerr(code));
+ return false;
+}
+
+//--------------------------------------------------------------------------
+// used to debug the debugger
+void debmod_t::debdeb(const char *format, ...)
+{
+ if ( !debug_debugger )
+ return;
+
+ va_list va;
+ va_start(va, format);
+ dvnotif(0, rpc, format, va);
+ va_end(va);
+}
+
+//--------------------------------------------------------------------------
+void debmod_t::cleanup(void)
+{
+ input_file_path.qclear();
+ old_ranges.qclear();
+ exceptions.qclear();
+ clear_debug_names();
+ handling_lowcnds.clear();
+#ifdef ENABLE_LOWCNDS
+ cndmap.clear();
+#endif
+ page_bpts.clear();
+ pid = 0;
+ is_dll = false;
+}
+
+//--------------------------------------------------------------------------
+void idaapi debmod_t::dbg_set_exception_info(const exception_info_t *table, int qty)
+{
+ exceptions.qclear();
+ exceptions.reserve(qty);
+ for ( int i=0; i < qty; i++ )
+ exceptions.push_back(*table++);
+}
+
+//--------------------------------------------------------------------------
+char *debug_event_str(const debug_event_t *ev)
+{
+ static char buf[MAXSTR];
+ return debug_event_str(ev, buf, sizeof(buf));
+}
+
+//--------------------------------------------------------------------------
+char *debug_event_str(const debug_event_t *ev, char *buf, size_t bufsize)
+{
+ char *ptr = buf;
+ char *end = buf + bufsize;
+ ptr += qsnprintf(ptr, end-ptr, "%s ea=%a",
+ get_event_name(ev->eid()),
+ ev->ea);
+ switch ( ev->eid() )
+ {
+ case PROCESS_STARTED: // New process started
+ case PROCESS_ATTACHED: // Attached to running process
+ case LIB_LOADED: // New library loaded
+ ptr += qsnprintf(ptr, end-ptr, " base=%a size=%a rebase=%a name=%s",
+ ev->modinfo().base,
+ ev->modinfo().size,
+ ev->modinfo().rebase_to,
+ ev->modinfo().name.c_str());
+ break;
+ case PROCESS_EXITED: // Process stopped
+ case THREAD_EXITED: // Thread stopped
+ ptr += qsnprintf(ptr, end-ptr, " exit_code=%d", ev->exit_code());
+ break;
+ case BREAKPOINT: // Breakpoint reached
+ ptr += qsnprintf(ptr, end-ptr, " hea=%a kea=%a", ev->bpt().hea, ev->bpt().kea);
+ break;
+ case EXCEPTION: // Exception
+ ptr += qsnprintf(ptr, end-ptr, " code=%x can_cont=%d ea=%a info=%s",
+ ev->exc().code,
+ ev->exc().can_cont,
+ ev->exc().ea,
+ ev->exc().info.c_str());
+ break;
+ case THREAD_STARTED: // thread name
+ case LIB_UNLOADED: // Library unloaded
+ case INFORMATION: // User-defined information
+ APPCHAR(ptr, end, ' ');
+ APPEND(ptr, end, ev->info().c_str());
+ break;
+ default:
+ break;
+ }
+ qsnprintf(ptr, end-ptr, " pid=%d tid=%d handled=%d",
+ ev->pid,
+ ev->tid,
+ ev->handled);
+ return buf;
+}
+
+//--------------------------------------------------------------------------
+//lint -e{1536} Exposing low access member 'debmod_t::dn_names'
+name_info_t *debmod_t::get_debug_names()
+{
+ return &dn_names;
+}
+
+//--------------------------------------------------------------------------
+int debmod_t::set_debug_names()
+{
+ name_info_t *ni = get_debug_names();
+ if ( ni->addrs.empty() )
+ return 1;
+ int code = send_debug_names_to_ida(
+ ni->addrs.begin(),
+ ni->names.begin(),
+ (int)ni->addrs.size());
+ clear_debug_names();
+ return code;
+}
+
+//--------------------------------------------------------------------------
+void debmod_t::clear_debug_names()
+{
+ if ( dn_names.addrs.empty() )
+ return;
+
+ typedef qvector charptr_vec_t;
+ charptr_vec_t::iterator it_end = dn_names.names.end();
+ for ( charptr_vec_t::iterator it=dn_names.names.begin(); it != it_end; ++it )
+ qfree(*it);
+
+ dn_names.names.clear();
+ dn_names.addrs.clear();
+}
+
+//--------------------------------------------------------------------------
+void debmod_t::save_debug_name(ea_t ea, const char *name)
+{
+ dn_names.addrs.push_back(ea);
+ dn_names.names.push_back(qstrdup(name));
+}
+
+//--------------------------------------------------------------------------
+ea_t debmod_t::find_debug_name(const char *name) const
+{
+ for ( size_t i = 0, size = dn_names.names.size(); i < size; i++ )
+ {
+ if ( streq(name, dn_names.names[i]) )
+ return dn_names.addrs[i];
+ }
+ return BADADDR;
+}
+
+//--------------------------------------------------------------------------
+bool debmod_t::continue_after_last_event(bool handled)
+{
+ last_event.handled = handled;
+ return dbg_continue_after_event(&last_event) == DRC_OK;
+}
+
+//--------------------------------------------------------------------------
+// if there is an active control bpt at the exception address,
+// unconditionally suspend the execution at it. continuing won't do any good
+// even if the user told us to continue after all exceptions.
+bool debmod_t::should_suspend_at_exception(
+ const debug_event_t *event,
+ const exception_info_t *ei)
+{
+ if ( ei == NULL || ei->break_on() )
+ return true;
+ appcalls_t::const_iterator p = appcalls.find(event->tid);
+ if ( p == appcalls.end() )
+ return false;
+ const call_contexts_t &ctxvec = p->second;
+ return !ctxvec.empty() && ctxvec.back().ctrl_ea == event->ea;
+}
+
+//--------------------------------------------------------------------------
+bool debmod_t::should_stop_appcall(thid_t, const debug_event_t *event, ea_t ea)
+{
+ switch ( event->eid() )
+ {
+ case EXCEPTION:
+ // exception on the stack (non-executable stack?)
+ if ( event->exc().ea == ea )
+ return true;
+ // no break
+ case BREAKPOINT:
+ // reached the control breakpoint?
+ if ( event->ea == ea )
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+//--------------------------------------------------------------------------
+// return top of the stack range usable by appcall. usually it is equal to the
+// current esp, unless the range below the stack pointer is not usable
+// (for example, AMD64 ABI required the "red zone" not to be modified)
+ea_t debmod_t::calc_appcall_stack(const regvals_t ®vals)
+{
+ return ea_t(regvals[sp_idx].ival);
+}
+
+//--------------------------------------------------------------------------
+ea_t idaapi debmod_t::dbg_appcall(
+ ea_t func_ea,
+ thid_t tid,
+ int stkarg_nbytes,
+ const struct regobjs_t *regargs,
+ struct relobj_t *stkbytes,
+ struct regobjs_t *retregs,
+ qstring *errbuf,
+ debug_event_t *_event,
+ int options)
+{
+ enum
+ {
+ E_OK, // Success
+ E_READREGS, // Failed to read registers
+ E_NOSTACK, // Failed to allocate stack frame
+ E_REG_USED, // The calling convention refers to reserved registers
+ E_ARG_ALLOC, // Failed to allocate memory for stack arguments
+ E_WRITE_ARGS, // Failed to setup stack arguments
+ E_WRITE_REGS, // Failed to setup register arguments
+ E_HANDLE_EVENT,// Failed to handle debug event
+ E_DEBUG_EVENT, // Could not get debug events
+ E_RESUME, // Failed to resume the application
+ E_EXCEPTION, // An exception has occurred
+ E_APPCALL_FROM_EXC, // Cannot issue an AppCall if last event was an exception
+ E_TIMEOUT, // Timeout
+ };
+
+ static const char *const errstrs[] =
+ {
+ "success",
+ "failed to read registers",
+ "failed to allocate stack frame",
+ "the calling convention refers to reserved registers",
+ "failed to allocate memory for stack arguments",
+ "failed to setup stack arguments",
+ "failed to setup register arguments",
+ "failed to handle debug event",
+ "could not get debug events",
+ "failed to resume the application",
+ "an exception has occurred",
+ "last event was an exception, cannot perform an appcall",
+ "timeout",
+ };
+
+ // Save registers
+ regval_t rv;
+
+ bool brk = false;
+ int err = E_OK;
+
+ call_context_t &ctx = appcalls[tid].push_back();
+
+ regval_map_t call_regs;
+ ea_t args_sp = BADADDR;
+ do
+ {
+ // In Win32, when WaitForDebugEvent() returns an exception
+ // it seems that the OS remembers the exception context so that
+ // the next call to ContinueDebugEvent() will work with the last exception context.
+ // Now if we carry an AppCall when an exception was just reported:
+ // - Appcall will change context
+ // - Appcall's control bpt will generate an exception thus overriding the last exception context saved by the OS
+ // - After Appcall, IDA kernel cannot really continue from the first exception because it was overwritten
+ // Currently we will disallow Appcalls if last event is an exception
+ if ( last_event.eid() == EXCEPTION )
+ {
+ err = E_APPCALL_FROM_EXC;
+ break;
+ }
+ // Save registers
+ ctx.saved_regs.resize(nregs);
+ if ( dbg_read_registers(tid, -1, ctx.saved_regs.begin(), NULL) != DRC_OK )
+ {
+ err = E_READREGS;
+ break;
+ }
+
+ // Get SP value
+ ea_t org_sp = calc_appcall_stack(ctx.saved_regs);
+ if ( org_sp == BADADDR )
+ {
+ err = E_NOSTACK;
+ break;
+ }
+
+ // Stack contents
+ bytevec_t stk;
+
+ // Prepare control address
+ // We will generate a BP code ptrsz aligned and push unto the stack
+ // as the first argument. This is where we will set the control bpt.
+ // Since the bpt is on the stack, two possible scenarios:
+ // - BPT exception
+ // - Access violation: trying to execute from NX page
+ // In both cases we will catch an exception and learn what address was
+ // involved.
+
+ // - Save the ctrl address
+ ea_t ctrl_ea = org_sp - debapp_attrs.addrsize;
+
+ // - Compute the pointer where arguments will be allocated on the stack
+ size_t stkbytes_size = align_up(stkbytes->size(), debapp_attrs.addrsize);
+ args_sp = ctrl_ea - stkbytes_size;
+
+ // align the stack pointer to 16 byte boundary (gcc compiled programs require it)
+ args_sp &= ~15;
+ ctx.ctrl_ea = args_sp + stkbytes_size;
+
+ // Relocate the stack arguments
+ if ( !stkbytes->relocate(args_sp, false) )
+ {
+ err = E_ARG_ALLOC;
+ break;
+ }
+
+ // Prepare the stack.
+ // The memory layout @SP before transfering to the function:
+ // R = ret addr
+ // A = args
+
+ // - Append the return address (its value is the value of the ctrl code address)
+ stk.append(&ctx.ctrl_ea, debapp_attrs.addrsize);
+
+ // - Append the stack args
+ stk.append(stkbytes->begin(), stkbytes->size());
+ stk.resize(debapp_attrs.addrsize+stkbytes_size); // align up
+ ctx.sp = args_sp - debapp_attrs.addrsize;
+ if ( ctx.sp >= org_sp )
+ { // underflow?
+ err = E_NOSTACK;
+ break;
+ }
+
+ int delta = finalize_appcall_stack(ctx, call_regs, stk);
+ ctx.sp += delta; // nbytes inserted at the beginning of stk
+
+ // Write the stack
+ int nwrite = stk.size() - delta;
+ if ( nwrite > 0 )
+ {
+ if ( dbg_write_memory(ctx.sp, stk.begin()+delta, nwrite, NULL) != nwrite )
+ {
+ err = E_WRITE_ARGS;
+ break;
+ }
+ //show_hex(stk.begin()+delta, nwrite, "Written stack bytes to %a:\n", ctx.sp);
+ }
+
+ // ask the debugger to set a breakpoint
+ dbg_add_bpt(NULL, BPT_SOFT, ctx.ctrl_ea, -1);
+
+ // Copy arg registers to call_regs
+ for ( size_t i=0; i < regargs->size(); i++ )
+ {
+ const regobj_t &ri = regargs->at(i);
+ int reg_idx = ri.regidx;
+ if ( reg_idx == sp_idx || reg_idx == pc_idx )
+ {
+ brk = true;
+ err = E_REG_USED;
+ break;
+ }
+
+ // Copy the register value
+ if ( ri.size() <= sizeof(rv.fval) )
+ {
+ rv.clear();
+ memcpy(rv.fval, ri.value.begin(), ri.size());
+ if ( ri.relocate )
+ rv.ival += args_sp;
+ }
+ else
+ {
+ bytevec_t &b = rv.set_bytes();
+ b.resize(ri.size());
+ memcpy(b.begin(), ri.value.begin(), ri.size());
+ rv.rvtype = 0; // custom data format
+ }
+ call_regs[reg_idx] = rv;
+ }
+ if ( brk )
+ break;
+
+ // Set the stack pointer
+ rv.clear();
+ rv.ival = ctx.sp;
+ call_regs[sp_idx] = rv;
+
+ // Set the instruction pointer
+ rv.ival = func_ea;
+ call_regs[pc_idx] = rv;
+
+ // Change all the registers in question
+ for ( regval_map_t::iterator it = call_regs.begin();
+ it != call_regs.end();
+ ++it )
+ {
+ if ( dbg_write_register(tid, it->first, &it->second, NULL) != DRC_OK )
+ {
+ err = E_WRITE_REGS;
+ brk = true;
+ break;
+ }
+ // Mark that we changed the regs already
+ ctx.regs_spoiled = true;
+ }
+ if ( brk )
+ break;
+
+ // For manual appcall, we have done everything, just return now
+ if ( (options & APPCALL_MANUAL) != 0 )
+ break;
+
+ // Resume the application
+ // Since no* exception last occurred**, we can safely say that the
+ // debugger actually handled the exception.
+ // * : We disallow appcalls if an exception last occurred
+ // **: Actually if an AppCall was issued then last event is an exception
+ // but we will mask it by calling continue_after_event(handled_by_debugger = true)
+ if ( !continue_after_last_event(true) )
+ {
+ err = E_RESUME;
+ break;
+ }
+
+ // We use this list to accumulate the events
+ // We will give back the events at the end of the loop
+ debug_event_t tmp;
+ debug_event_t *event = _event != NULL ? _event : &tmp;
+
+ // Determine timeout for get_debug_event()
+ uint64 endtime = 0;
+ int recalc_timeout = 0; // never recalc timeout
+ int timeout_ms = TIMEOUT;
+ if ( (options & APPCALL_TIMEOUT) != 0 )
+ {
+ timeout_ms = GET_APPCALL_TIMEOUT(options);
+ if ( timeout_ms > 0 )
+ {
+ endtime = get_nsec_stamp();
+ endtime += timeout_ms * uint64(1000 * 1000);
+ }
+ recalc_timeout = 1; // recalc timeout after the first pass
+ }
+
+ while ( true )
+ {
+ if ( recalc_timeout )
+ {
+ if ( recalc_timeout != 2 )
+ { // we will recalc timeout at the next iteration
+ recalc_timeout = 2;
+ }
+ else
+ {
+ if ( timeout_ms > 0 )
+ {
+ // calculate the remaining timeout
+ uint64 now = get_nsec_stamp();
+ timeout_ms = int64(endtime - now) / int64(1000 * 1000);
+ }
+ if ( timeout_ms <= 0 )
+ { // timeout out waiting for the appcall to finish
+ err = E_TIMEOUT;
+ if ( dbg_prepare_to_pause_process(NULL) <= DRC_NONE )
+ break; // could not even prepare to pause, nothing we can do :(
+ }
+ }
+ }
+ // Wait for debug events
+ gdecode_t r = dbg_get_debug_event(event, timeout_ms);
+ if ( r == GDE_NO_EVENT )
+ continue;
+ if ( r == GDE_ERROR )
+ { // error getting debug event (network error, etc)
+ err = E_DEBUG_EVENT;
+ break;
+ }
+
+ // We may get three possible events related to our control breakpoint:
+ // - Access violation type: because we try to execute non-executable code
+ // - Or a BPT exception if the stack page happens to be executable
+ // - Process exit
+ if ( event->eid() == PROCESS_EXITED // process is gone
+ || event->eid() == THREAD_EXITED && event->tid == tid ) // our thread is gone
+ {
+ send_debug_event_to_ida(event, RQ_SILENT);
+ args_sp = BADADDR;
+ brk = true;
+ break;
+ }
+ if ( err == E_TIMEOUT )
+ {
+ send_debug_event_to_ida(event, RQ_SILENT|RQ_SUSPEND);
+ event->set_eid(NO_EVENT);
+ last_event.set_eid(NO_EVENT);
+ break;
+ }
+ if ( should_stop_appcall(tid, event, ctx.ctrl_ea) )
+ {
+ last_event.set_eid(NO_EVENT);
+ break;
+ }
+ // Any other exception?
+ if ( event->eid() == EXCEPTION )
+ {
+ if ( (options & APPCALL_DEBEV) == 0 )
+ *errbuf = event->exc().info; // Copy exception text to the user
+ err = E_EXCEPTION;
+ // When an exception happens during the appcall, we want to mask
+ // the exception, because:
+ // 1. we reset the EIP to its original location
+ // 2. there is no exception handler for the appcall so we cannot really pass as unhandled
+ // FIXME
+ last_event.set_eid(NO_EVENT);
+ last_event.handled = true;
+ brk = true;
+ break;
+ }
+
+ if ( send_debug_event_to_ida(event, RQ_SILENT|RQ_SUSPEND) != 0 )
+ {
+ err = E_HANDLE_EVENT;
+ break;
+ }
+ dbg_continue_after_event(event);
+ event->set_eid(NO_EVENT);
+ }
+
+ if ( brk || err != E_OK )
+ break;
+
+ // write the argument vector back because it could be spoiled by the application
+ if ( stkarg_nbytes > 0 )
+ {
+ int nbytes = stkarg_nbytes;
+ if ( nbytes > stkbytes->size() // wrong parameters
+ || dbg_write_memory(args_sp, stkbytes->begin(), nbytes, NULL) != ssize_t(nbytes) )
+ {
+ err = E_WRITE_ARGS;
+ break;
+ }
+ }
+
+ // Retrieve the return value
+ if ( retregs != NULL && !retregs->empty() )
+ {
+ regvals_t retr;
+ retr.resize(nregs);
+ if ( dbg_read_registers(tid, -1, retr.begin(), NULL) <= DRC_NONE )
+ {
+ err = E_READREGS;
+ break;
+ }
+ for ( size_t i=0; i < retregs->size(); i++ )
+ {
+ regobj_t &r = retregs->at(i);
+ regval_t &v = retr[r.regidx];
+ memcpy(r.value.begin(), v.get_data(), r.value.size());
+ r.relocate = false;
+ }
+ }
+ } while ( false );
+
+ if ( err != E_OK )
+ {
+ if ( err != E_EXCEPTION )
+ *errbuf = errstrs[err];
+ dbg_cleanup_appcall(tid);
+ args_sp = BADADDR;
+ }
+
+ return args_sp;
+}
+
+//--------------------------------------------------------------------------
+// Cleanup after appcall()
+// The debugger module must keep the stack blob in the memory until this function
+// is called. It will be called by the kernel for each successful call_app_func()
+drc_t idaapi debmod_t::dbg_cleanup_appcall(thid_t tid)
+{
+ appcalls_t::iterator p = appcalls.find(tid);
+ if ( p == appcalls.end() )
+ return DRC_FAILED;
+ call_contexts_t &calls = p->second;
+ if ( calls.empty() )
+ return DRC_FAILED;
+
+ // remove the return breakpoint
+ call_context_t &ctx = calls.back();
+ if ( !preprocess_appcall_cleanup(tid, ctx) )
+ return DRC_FAILED;
+
+ dbg_del_bpt(BPT_SOFT, ctx.ctrl_ea, bpt_code.begin(), bpt_code.size());
+ if ( ctx.regs_spoiled )
+ {
+ if ( !write_registers(tid, 0, ctx.saved_regs.size(), ctx.saved_regs.begin()) )
+ {
+ dmsg("Failed to restore %" FMT_Z " registers!\n", ctx.saved_regs.size());
+ return DRC_FAILED;
+ }
+ }
+
+ calls.pop();
+ if ( calls.empty() )
+ appcalls.erase(p);
+ return events.empty() ? DRC_OK : DRC_EVENTS;
+}
+
+//--------------------------------------------------------------------------
+drc_t debmod_t::resume_app_and_get_event(debug_event_t *dev)
+{
+ thid_t tid = dev->tid;
+ drc_t drc = dbg_continue_after_event(dev);
+ if ( drc > DRC_NONE )
+ {
+ while ( true )
+ {
+ gdecode_t gc = dbg_get_debug_event(dev, TIMEOUT_INFINITY);
+ if ( gc != GDE_NO_EVENT )
+ break;
+ }
+ // is it in our thread?
+ if ( tid != dev->tid )
+ { // very odd! an event from another thread arrived
+ if ( dev->eid() != THREAD_STARTED )
+ dmsg("unexpected event from thread %d arrived (expected thread %d)\n", dev->tid, tid);
+ drc = DRC_FAILED; // indicate failure
+ }
+ }
+ return drc;
+}
+
+//--------------------------------------------------------------------------
+drc_t debmod_t::dbg_perform_single_step(debug_event_t *dev, const insn_t &)
+{
+ // all other threads must be frozen at this moment
+ drc_t drc = dbg_set_resume_mode(dev->tid, RESMOD_INTO);
+ if ( drc > DRC_NONE )
+ drc = resume_app_and_get_event(dev);
+ return drc;
+}
+
+//--------------------------------------------------------------------------
+// returns true-lowcnd was false, resumed the application
+// nb: recursive calls to this function are not handled in any special way!
+bool debmod_t::handle_lowcnd(lowcnd_t *lc, debug_event_t *event, int elc_flags)
+{
+ if ( (debugger_flags & DBG_FLAG_CAN_CONT_BPT) == 0
+ || lc->type != BPT_SOFT && find_page_bpt(lc->ea, lc->size) != page_bpts.end() )
+ {
+ // difficult case: we have to reset pc, remove the bpt, single step, and resume the app
+ QASSERT(616, !handling_lowcnds.has(lc->ea));
+ handling_lowcnds.push_back(lc->ea);
+
+ if ( (elc_flags & ELC_KEEP_EIP) == 0 )
+ {
+ regval_t rv;
+ rv._set_int(lc->ea);
+ drc_t drc = dbg_write_register(event->tid, pc_idx, &rv, NULL);
+ if ( drc <= DRC_NONE )
+ {
+ handling_lowcnds.del(lc->ea);
+ return false;
+ }
+ }
+
+ int code = dbg_freeze_threads_except(event->tid);
+ if ( code > 0 )
+ {
+ int bptlen = lc->type == BPT_SOFT ? lc->orgbytes.size() : lc->size;
+ code = dbg_del_bpt(lc->type, lc->ea, lc->orgbytes.begin(), bptlen);
+ if ( code > 0 )
+ {
+ drc_t drc = dbg_perform_single_step(event, lc->cmd);
+ if ( drc <= DRC_NONE )
+ {
+ code = 0;
+ dmsg("%a: failed to single step\n", event->ea); // may happen
+ }
+
+ if ( dbg_add_bpt(NULL, lc->type, lc->ea, bptlen) <= 0 )
+ {
+ // if this fails, it may be because the breakpoint is invalid
+ // at this time so we should notify IDA it isn't available
+ // any more
+ code = 0;
+ dwarning("%a: could not restore deleted bpt\n", lc->ea); // odd
+ }
+ }
+ if ( dbg_thaw_threads_except(event->tid) <= 0 )
+ {
+ dwarning("%d: could not resume suspended threads\n", event->tid); // odd
+ code = 0;
+ }
+ }
+ handling_lowcnds.del(lc->ea);
+ if ( code <= 0 || event->eid() != STEP )
+ return false; // did not resume
+ }
+ if ( (elc_flags & ELC_KEEP_SUSP) != 0 )
+ return true;
+ return dbg_continue_after_event(event) > DRC_NONE;
+}
+
+//--------------------------------------------------------------------------
+// return lowcnd_t if its condition is not satisfied
+lowcnd_t *debmod_t::get_failed_lowcnd(thid_t tid, ea_t ea)
+{
+#ifndef ENABLE_LOWCNDS
+ //lint -esym(1762, debmod_t::get_failed_lowcnd) could be made const
+ qnotused(tid);
+ qnotused(ea);
+#else
+ lowcnds_t::iterator p = cndmap.find(ea);
+ if ( p != cndmap.end() )
+ {
+ bool ok = true;
+ idc_value_t rv;
+ char name[32];
+ ::qsnprintf(name, sizeof(name), "__lc%a", ea);
+ lowcnd_t &lc = p->second;
+ lock_begin();
+ {
+ idc_debmod = this; // is required by compiler/interpreter
+ idc_thread = tid; // is required by interpreter
+ if ( !lc.compiled )
+ {
+ ok = compile_idc_snippet(name, lc.cndbody.begin(), NULL, NULL, true);
+ if ( ok )
+ lc.compiled = true;
+ }
+ if ( ok )
+ ok = call_idc_func(&rv, name, NULL, 0);
+ }
+ lock_end();
+ if ( !ok )
+ {
+ report_idc_error(ea, get_qerrno(), get_error_data(0), get_error_string(0));
+ return NULL;
+ }
+
+ idcv_int64(&rv);
+ if ( rv.i64 == 0 )
+ return &lc; // condition is not satisfied, resume
+ }
+#endif
+ return NULL;
+}
+
+//--------------------------------------------------------------------------
+bool debmod_t::evaluate_and_handle_lowcnd(debug_event_t *event, int elc_flags)
+{
+ bool resume = false;
+ if ( event->eid() == BREAKPOINT )
+ {
+ ea_t ea = event->bpt().kea != BADADDR ? event->bpt().kea
+ : event->bpt().hea != BADADDR ? event->bpt().hea
+ : event->ea;
+ lowcnd_t *lc = get_failed_lowcnd(event->tid, ea);
+ if ( lc != NULL )
+ { // condition is not satisfied, just make a single step and resume
+ debdeb("%a: bptcnd yielded false\n", ea);
+ event->handled = true;
+ QASSERT(617, !handling_lowcnds.has(ea));
+ resume = handle_lowcnd(lc, event, elc_flags);
+ }
+ }
+ return resume;
+}
+
+//--------------------------------------------------------------------------
+drc_t idaapi debmod_t::dbg_eval_lowcnd(thid_t tid, ea_t ea, qstring * /*errbuf*/)
+{
+ return get_failed_lowcnd(tid, ea) == NULL ? DRC_OK : DRC_FAILED;
+}
+
+//--------------------------------------------------------------------------
+drc_t idaapi debmod_t::dbg_update_lowcnds(
+ int *nupdated,
+ const lowcnd_t *lowcnds,
+ int nlowcnds,
+ qstring * /*errbuf*/)
+{
+#ifndef ENABLE_LOWCNDS
+ qnotused(lowcnds);
+ qnotused(nlowcnds);
+ if ( nupdated != NULL )
+ *nupdated = 0;
+ return DRC_OK;
+#else
+ for ( int i=0; i < nlowcnds; i++, lowcnds++ )
+ {
+ ea_t ea = lowcnds->ea;
+ if ( lowcnds->cndbody.empty() )
+ cndmap.erase(ea);
+ else
+ cndmap[ea] = *lowcnds;
+ }
+ if ( nupdated != NULL )
+ *nupdated = nlowcnds;
+ return DRC_OK;
+#endif
+}
+
+//--------------------------------------------------------------------------
+// determine the future bpt size and read the original instruction from memory.
+// if necessary, the bpt address may be adjusted.
+// return the number of read bytes.
+// this function is overloaded in arm debugger subclasses.
+int debmod_t::read_bpt_orgbytes(bytevec_t *buf, ea_t ea, int len)
+{
+ buf->qclear();
+ if ( (debugger_flags & DBG_FLAG_CAN_CONT_BPT) == 0 )
+ { // we must save the original bytes before adding the bpt
+ buf->resize(len);
+ if ( dbg_read_memory(ea, buf->begin(), len, NULL) <= 0 )
+ return -1;
+ }
+ else
+ { // if the debuger can itself continue from bpts,
+ // orgbytes will not be used by the kernel. however
+ // we must return something because non-empty orgbytes mean
+ // that a soft bpt is active. we return zeroes in b->orgbytes.
+ buf->resize(len, 0);
+ }
+ return len;
+}
+
+//--------------------------------------------------------------------------
+drc_t idaapi debmod_t::dbg_update_bpts(
+ int *nbpts,
+ update_bpt_info_t *ubpts,
+ int nadd,
+ int ndel,
+ qstring * /*errbuf*/)
+{
+ if ( events.empty() )
+ deleted_bpts.clear();
+
+ // Write breakpoints to the process
+ int cnt = 0;
+ update_bpt_info_t *b;
+ update_bpt_info_t *end = ubpts + nadd;
+ for ( b=ubpts; b != end; b++ )
+ {
+ int code = b->code;
+ if ( code != BPT_OK )
+ continue; // should be BPT_SKIP
+
+ int len = b->type == BPT_SOFT ? bpt_code.size() : b->size;
+ ea_t ea = b->ea;
+ if ( b->type == BPT_SOFT )
+ adjust_swbpt(&ea, &len);
+
+ update_bpt_vec_t::iterator d = deleted_bpts.find(*b);
+ if ( d != deleted_bpts.end() && d->tid == b->tid )
+ deleted_bpts.erase(d);
+
+ code = dbg_add_bpt(&b->orgbytes, b->type, ea, len);
+ debdeb("dbg_add_bpt(type=%d, ea=%a, len=%d) => %d\n", b->type, ea, len, code);
+ switch ( code )
+ {
+ case 2:
+ code = BPT_PAGE_OK;
+ break;
+ case 1:
+ code = BPT_OK;
+ break;
+ default:
+ code = BPT_WRITE_ERROR;
+ break;
+ case -2:
+ code = BPT_READ_ERROR;
+ break;
+ }
+
+ b->code = code;
+ if ( code == BPT_OK || code == BPT_PAGE_OK )
+ cnt++;
+ }
+
+ // Delete breakpoints from the process.
+ end += ndel;
+ for ( ; b != end; b++ )
+ {
+ b->code = BPT_OK;
+ int len = b->type == BPT_SOFT ? b->orgbytes.size() : b->size;
+ int code = dbg_del_bpt(b->type, b->ea, b->orgbytes.begin(), len);
+ debdeb("dbg_del_bpt(type=%d, ea=%a) => %d\n", b->type, b->ea, code);
+ if ( code > 0 )
+ {
+ cnt++;
+ if ( b->type == BPT_SOFT || b->type == BPT_EXEC )
+ deleted_bpts.push_back(*b);
+ }
+ else
+ {
+ b->code = BPT_WRITE_ERROR;
+ }
+ }
+
+ if ( nbpts != NULL )
+ *nbpts = cnt;
+ return DRC_OK;
+}
+
+//--------------------------------------------------------------------------
+// Find a page breakpoint that would cause an exception on the given range
+// NB: we do not check the user-specified range but the real page range!
+page_bpts_t::iterator debmod_t::find_page_bpt(
+ ea_t ea,
+ int size)
+{
+ page_bpts_t::iterator p = page_bpts.lower_bound(calc_page_base(ea));
+ if ( p == page_bpts.end() || p->first >= ea+size )
+ {
+ if ( p == page_bpts.begin() )
+ return page_bpts.end(); // not found
+ --p;
+ }
+ ea_t page_ea = p->first;
+ int page_len = p->second.aligned_len;
+ if ( !interval::overlap(ea, size, page_ea, page_len) )
+ p = page_bpts.end();
+ return p;
+}
+
+//--------------------------------------------------------------------------
+bool debmod_t::del_page_bpt(ea_t ea, bpttype_t type)
+{
+ page_bpts_t::iterator p = find_page_bpt(ea);
+ if ( p == page_bpts.end() )
+ return false; // could not find
+ if ( p->second.ea != ea || p->second.type != type )
+ return false; // not exact match
+
+ dbg_enable_page_bpt(p, false);
+ page_bpts.erase(p);
+ return true;
+}
+
+//--------------------------------------------------------------------------
+void debmod_t::enable_page_bpts(bool enable)
+{
+ for ( page_bpts_t::iterator p = page_bpts.begin(); p != page_bpts.end(); ++p )
+ dbg_enable_page_bpt(p, enable);
+}
+
+//--------------------------------------------------------------------------
+void debmod_t::set_platform(const char *platform_name)
+{
+ debapp_attrs.platform = platform_name;
+}
+
+//--------------------------------------------------------------------------
+void debmod_t::dbg_get_debapp_attrs(debapp_attrs_t *out_pattrs) const
+{
+ *out_pattrs = debapp_attrs;
+}
+
+//--------------------------------------------------------------------------
+bool debmod_t::restore_broken_breakpoints(void)
+{
+ debmodbpt_map_t::const_iterator p;
+ for ( p = bpts.begin(); p != bpts.end(); ++p )
+ {
+ const debmod_bpt_t &bpt = p->second;
+ if ( !dbg_write_memory(bpt.ea, bpt.saved, bpt.nsaved, NULL) )
+ msg("Failed to restore broken breakpoint at 0x%a\n", bpt.ea);
+ }
+ bpts.clear();
+ return true;
+}
+
+
+//--------------------------------------------------------------------------
+void debmod_t::log_exception(
+ const debug_event_t *ev,
+ const exception_info_t *ei)
+{
+ if ( ei == NULL || (ei->flags & EXC_SILENT) == 0 )
+ {
+ if ( ev->exc().ea != BADADDR )
+ {
+ dmsg("%a: %s -> %a (exc.code %x, tid %d)\n",
+ ev->ea, ev->exc().info.c_str(), ev->exc().ea, ev->exc().code, ev->tid);
+ }
+ else
+ {
+ dmsg("%a: %s (exc.code %x, tid %d)\n",
+ ev->ea, ev->exc().info.c_str(), ev->exc().code, ev->tid);
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+int idaapi debmod_t::dbg_rexec(const char *cmdline)
+{
+ msg("REXEC: %s\n", cmdline);
+ return call_system(cmdline);
+}
+
+//--------------------------------------------------------------------------
+drc_t idaapi debmod_t::dbg_get_processes(procinfo_vec_t *procs, qstring *errbuf)
+{
+ proclist.qclear();
+ get_process_list(&proclist, errbuf);
+
+ size_t size = proclist.size();
+ procs->qclear();
+ procs->reserve(size);
+ for ( size_t i = 0; i < size; i++ )
+ {
+ process_info_t &procinf = procs->push_back();
+ proclist[i].copy_to(&procinf);
+ }
+
+ return DRC_OK;
+}
+
+//--------------------------------------------------------------------------
+int idaapi debmod_t::get_process_list(procvec_t *list, qstring *)
+{
+ list->clear();
+ return -1;
+}
+
+//--------------------------------------------------------------------------
+uint64 debmod_t::probe_file_size(int fn, uint64 step)
+{
+ uint64 low = 0;
+ uint64 high = step;
+ char dummy;
+ while ( dbg_read_file(fn, high-1, &dummy, 1) == 1 )
+ {
+ low = high;
+ high += step;
+ if ( step < 0x7FFFFFFFFFFFFFFFULL )
+ step *= 2;
+ }
+ while ( low+1 < high )
+ {
+ int middle = (high+low)/2;
+ if ( dbg_read_file(fn, middle-1, &dummy, 1) == 1 )
+ low = middle;
+ else
+ high = middle;
+ }
+ return low;
+}
diff --git a/idasdk75/dbg/debmod.h b/idasdk75/dbg/debmod.h
new file mode 100644
index 0000000..62957e6
--- /dev/null
+++ b/idasdk75/dbg/debmod.h
@@ -0,0 +1,741 @@
+#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