update to ida 7.6, add builds

This commit is contained in:
2021-10-31 21:20:46 +02:00
parent e0e0f2be99
commit b1809fe2d9
1408 changed files with 279193 additions and 302468 deletions

855
idasdk76/allmake.mak Normal file
View File

@@ -0,0 +1,855 @@
#
# Common part of make files for IDA.
#
# find directory of allmake.mak:
IDA:=$(dir $(lastword $(MAKEFILE_LIST)))
# define the version number we are building
IDAVER_MAJOR:=7
IDAVER_MINOR:=6
# 760
IDAVERDECIMAL:=$(IDAVER_MAJOR)$(IDAVER_MINOR)0
# 7.6
IDAVERDOTTED:=$(IDAVER_MAJOR).$(IDAVER_MINOR)
# only 32-bit cygwin make is support on Windows
ifeq ($(OS),Windows_NT)
ifneq ($(MAKE_HOST),i686-pc-cygwin)
$(error Only 32-bit cygwin make is supported on Windows. Make sure you are not using 64-bit cygwin, msys, msys2, or any other version of win32 make)
endif
endif
# if no targets are defined, default to host OS
ifeq ($(or $(__ANDROID__),$(__ANDROID_X86__),$(__ARMLINUX__),$(__LINUX__),$(__MAC__),$(__NT__)),)
ifeq ($(OS),Windows_NT)
__NT__=1
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
__LINUX__=1
endif
ifeq ($(UNAME_S),Darwin)
__MAC__=1
endif
endif
endif
# only one build target may be defined
ifneq ($(__ANDROID__)$(__ANDROID_X86__)$(__ARMLINUX__)$(__LINUX__)$(__MAC__)$(__NT__),1)
$(error Only one build target may be defined (__ANDROID__, __ANDROID_X86__, __ARMLINUX__, __LINUX__, __MAC__, or __NT__))
endif
# detect build configuration
# Note: will set one of M, MM, MMH, M32, MO, MMO, MMHO, MO32, MSO, MMSO, or MSO32
BUILD_CONFIG-1 := M
BUILD_CONFIG-$(__EA64__) += M
BUILD_CONFIG-$(USE_STATIC_RUNTIME) += S
BUILD_CONFIG-$(IDAHOME) += H
BUILD_CONFIG-$(NDEBUG) += O
BUILD_CONFIG-$(__X86__) += 32
empty :=
space := $(empty) $(empty)
comma := ,
BUILD_CONFIG := $(subst $(space),,$(BUILD_CONFIG-1))
$(BUILD_CONFIG) := 1
# definition of a single \n character (empty lines are important!)
define newline
endef
# support arm64 macOS11 builds
ifeq ($(and $(__MAC__),$(__ARM__)),1)
ifdef __X86__
$(error 32-bit ARM builds are not supported on mac.)
endif
# create a shortcut for convenience
__APPLE_SILICON__=1
endif
ifdef __ARM__
PROCDEF = __ARM__
TARGET_PROCESSOR_NAME-1=arm
TARGET_PROCESSOR_NAME-$(__MAC__)=arm64
TARGET_PROCESSOR_NAME=$(TARGET_PROCESSOR_NAME-1)
else ifndef __X86__
ARCH_FLAGS = -m64
TARGET_PROCESSOR_NAME=x64
else
ARCH_FLAGS = -m32
TARGET_PROCESSOR_NAME=x86
endif
# define some variables to simplify build system
ifndef __X86__
__X64__ = 1
ifndef __EA64__
__X32__ = 1
endif
endif
ifndef __NT__
__UNIX__ = 1
endif
ifndef IDAHOME
IDAADV = 1
endif
# define SYSNAME
SYSNAME-$(__LINUX__) = linux
SYSNAME-$(__MAC__) = mac
SYSNAME-$(__NT__) = win
SYSNAME = $(SYSNAME-1)
# path functions (depending on host OS)
ifeq ($(OS),Windows_NT)
# define: convert unix path to dos path by replacing slashes by backslashes
dospath=$(subst /,\\,$(1))
else
# define: dospath does not do anything in unix
dospath=$(1)
endif
# define: return 1 if path exists, 0 otherwise
ls=$(if $(wildcard $(1)),1,0)
# define: logical negation
not = $(if $(1),,1)
# define: greater or equal
gte = $(if $(filter-out $(1),$(word 2,$(sort $(1) $(2)))),,1)
include $(IDA)defaults.mk
#############################################################################
ifdef __NT__
COMPILER_NAME=vc
# Visual C++ Toolchain and Windows SDK paths
# Note: see comments in defaults.mk for more information about these
# variables.
# This function searches for a specified path, converts it to a 8.3
# path with forward slashes as separator, and exports it as an
# environment variable. This way, subcalls to make do not need to
# call $(shell) again.
define require_path
$$(if $(strip $$($(1))),,$$(eval $(1):=$$(subst \,/,$$(shell cygpath -d $(2) 2>/dev/null))))
$$(if $(strip $$($(1))),,$$(error Could not find $(3) in $(2)$$(newline)*** See defaults.mk and "Visual C++ Toolchain and Windows SDK paths" in allmake.mak))
$$(eval export $(1))
endef
# This function fixes variables imported from defaults.mk/vcvars.bat
# by ensuring that they are surrounded by quotes and by removing the
# trailing backslash.
fix_var=$(1):='$$(patsubst %\,%,$$(patsubst '%,%,$$(patsubst %',%,$$(patsubst "%,%,$$(patsubst %",%,$$($(1)))))))'
# Note: these cfg files are created in makeenv_vc.mak
ifdef __XPCOMPAT__
-include $(IDA)vs17paths_xp.cfg
else
-include $(IDA)vs17paths.cfg
endif
# Visual C++ 2017 Install Directory
ifndef MSVC_ROOT
ifneq (,$(findstring Microsoft$(space)Visual$(space)Studio$(space),$(VCINSTALLDIR)))
ifeq (,$(findstring 2017,$(VCINSTALLDIR)))
$(error Please check your system environment variable VCInstallDir [$(VCINSTALLDIR)].$(newline)It seems to be pointing to an old version of Visual Studio (and not version 2017).$(newline)You may override it in defaults.mk.)
endif
endif
$(eval $(call fix_var,VCINSTALLDIR))
$(eval $(call require_path,MSVC_ROOT,$(VCINSTALLDIR),Visual C++ 2017 Install Directory))
export MSVC_ROOT
endif
# Visual C++ 2017 Tools Version
ifndef MSVC_TOOLSVER
ifndef VCToolsVersion
# Try to obtain version from Microsoft.VCToolsVersion.default.txt
MSVC_TOOLSVER_PATH = $(MSVC_ROOT)/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt
VCToolsVersion := $(shell cat $(MSVC_TOOLSVER_PATH) 2> /dev/null)
ifeq (,$(VCToolsVersion))
# If that failed, try to detect latest version from the directory names
VCToolsVersion := $(notdir $(lastword $(sort $(wildcard $(MSVC_ROOT)/Tools/MSVC/14.*))))
endif
ifeq (,$(VCToolsVersion))
$(error Could not find Visual C++ 2017 Tools Version in $(MSVC_TOOLSVER_PATH))
endif
endif
$(eval $(call fix_var,VCToolsVersion))
MSVC_TOOLSVER := $(VCToolsVersion)
export MSVC_TOOLSVER
endif
# Final Visual C++ 2017 Tools path
$(eval $(call require_path,MSVC_PATH,$(MSVC_ROOT)/Tools/MSVC/$(MSVC_TOOLSVER),Visual C++ 2017 Tools))
MSVC_BIN-X86 ?= $(MSVC_PATH)/bin/HostX86/x86
MSVC_BIN-X64 ?= $(MSVC_PATH)/bin/HostX64/x64
ifdef __X86__
MSVC_BIN ?= $(MSVC_BIN-X86)
else
MSVC_BIN ?= $(MSVC_BIN-X64)
endif
MSVC_INCLUDE ?= $(MSVC_PATH)/Include
# Windows SDK Install Directory
ifndef WSDK_PATH
$(eval $(call fix_var,WindowsSdkDir))
$(eval $(call require_path,WSDK_PATH,$(WindowsSdkDir),Windows SDK Install Directory))
export WSDK_PATH
endif
# Windows SDK Version
ifndef WSDK_VER
ifndef WindowsSDKVersion
# Detect the latest version of the Windows SDK
WSDK_VER_PATH = $(WSDK_PATH)/Include/10.*
WindowsSDKVersion := $(notdir $(lastword $(sort $(wildcard $(WSDK_VER_PATH)))))
ifeq (,$(WindowsSDKVersion))
$(error Could not find Windows SDK Version in $(WSDK_VER_PATH))
endif
endif
$(eval $(call fix_var,WindowsSDKVersion))
WSDK_VER := $(WindowsSDKVersion)
export WSDK_VER
endif
# Windows SDK Include/Lib paths
INCLUDE_UCRT_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/ucrt
LIB_UCRT_PATH ?= $(WSDK_PATH)/Lib/$(WSDK_VER)/ucrt
$(eval $(call require_path,INCLUDE_UCRT,$(INCLUDE_UCRT_PATH),Windows SDK Include/ucrt))
$(eval $(call require_path,LIB_UCRT,$(LIB_UCRT_PATH),Windows SDK Lib/ucrt))
ifdef __XPCOMPAT__
$(eval $(call require_path,INCLUDE_MSSDK71,$(MSSDK71_PATH)/Include,Microsoft SDK Include))
$(eval $(call require_path,LIB_MSSDK71,$(MSSDK71_PATH)/Lib,Microsoft SDK Lib))
$(eval $(call require_path,SDK_BIN,$(MSSDK71_PATH)/Bin,Microsoft SDK Bin))
else
INCLUDE_SHARED_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/shared
INCLUDE_UM_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/um
LIB_UM_PATH ?= $(WSDK_PATH)/Lib/$(WSDK_VER)/um
SDK_BIN_PATH ?= $(WSDK_PATH)/Bin/$(WSDK_VER)/
$(eval $(call require_path,INCLUDE_SHARED,$(INCLUDE_SHARED_PATH),Windows SDK Include/shared))
$(eval $(call require_path,INCLUDE_UM,$(INCLUDE_UM_PATH),Windows SDK Include/um))
$(eval $(call require_path,LIB_UM,$(LIB_UM_PATH),Windows SDK Lib/um))
$(eval $(call require_path,SDK_BIN,$(SDK_BIN_PATH),Windows SDK Bin))
endif
# Export INCLUDE as an environment variable so it may be used by cl.
ifndef INCLUDE
ifdef __XPCOMPAT__
INCLUDE = $(MSVC_INCLUDE);$(INCLUDE_UCRT);$(INCLUDE_MSSDK71)
else
INCLUDE = $(MSVC_INCLUDE);$(INCLUDE_UCRT);$(INCLUDE_UM);$(INCLUDE_SHARED)
endif
export INCLUDE
endif
# Export LIB as an environment variable so it may be used by cl/link.
ifndef LIB
ifdef __XPCOMPAT__
ifdef __X86__
LIB = $(MSVC_PATH)/lib/x86;$(LIB_UCRT)/x86;$(LIB_MSSDK71)
else
LIB = $(MSVC_PATH)/lib/x64;$(LIB_UCRT)/x64;$(LIB_MSSDK71)/x64
endif
else
ifdef __X86__
LIB = $(MSVC_PATH)/lib/x86;$(LIB_UCRT)/x86;$(LIB_UM)/x86
else
LIB = $(MSVC_PATH)/lib/x64;$(LIB_UCRT)/x64;$(LIB_UM)/x64
endif
endif
export LIB
endif
# If a Visual Studio Command Prompt is used, make sure the target
# architecture is correct.
ifdef VSCMD_ARG_TGT_ARCH
ifneq ($(VSCMD_ARG_TGT_ARCH),$(TARGET_PROCESSOR_NAME))
ifdef __X86__
EXPECTED_ARCH = x86
else
EXPECTED_ARCH = x64
endif
LOWERCASE_BUILD_CONFIG := $(subst M,m,$(subst S,s,$(subst O,o,$(BUILD_CONFIG))))
$(error Please use the correct Visual Studio Command Prompt for the target architecture$(newline)*** The target architecture for '$(LOWERCASE_BUILD_CONFIG)' is $(EXPECTED_ARCH), and the architecture for the current Visual Studio Command Prompt is $(VSCMD_ARG_TGT_ARCH)))
endif
endif
#############################################################################
else ifdef __LINUX__
COMPILER_NAME=gcc
PTHR_SWITCH=-pthread
STDLIBS += -lrt -lpthread -lc
#############################################################################
else ifdef __MAC__
COMPILER_NAME=clang
STDLIBS += -lpthread -liconv
ARCH_FLAGS-$(__X64__) = -arch x86_64
ARCH_FLAGS-$(__X86__) = -arch i386
ARCH_FLAGS-$(__ARM__) = -arch arm64
ARCH_FLAGS += $(ARCH_FLAGS-1)
# The following value is defined in defaults.mk.
ARCH_FLAGS += -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET)
ifndef MACSDK
MACSDK := $(shell /usr/bin/xcrun --sdk macosx --show-sdk-path)
ifeq ($(MACSDK),)
$(error Could not find MacOSX SDK)
endif
export MACSDK
endif
ARCH_FLAGS += -isysroot $(MACSDK)
endif
#############################################################################
# toolchain-specific variables
ifneq (,$(filter $(COMPILER_NAME),gcc clang))
# file extensions
A = .a
B = $(SUFF64)
O = .o
II = .i
# toolchain output switches
OBJSW = -o # with space
OUTAR =
OUTII = -o # with space
OUTSW = -o # with space
ifdef __MAC__
OUTMAP = -Wl,-map,
else
OUTMAP = -Wl,-Map,
endif
# misc switches
AROPT = rc
CPPONLY = -E
FORCEC = -xc
NORTTI = -fno-rtti
ifdef __MAC__
OUTDLL = -dynamiclib
else
OUTDLL = --shared
endif
# utilities
CCACHE-$(USE_CCACHE) = ccache
ifeq ($(COMPILER_NAME),clang)
_CC = clang
_CXX = clang++
else
_CC = gcc
_CXX = g++
ifdef USE_GOLD
GOLD = -fuse-ld=gold
endif
endif
AR = $(CROSS_PREFIX)ar$(HOST_EXE) $(AROPT)
CC = $(CCACHE-1) $(CROSS_PREFIX)$(_CC)$(HOST_EXE) $(ARCH_FLAGS)
CCL = $(CROSS_PREFIX)$(_CXX)$(HOST_EXE) $(ARCH_FLAGS) $(GOLD)
CXX = $(CCACHE-1) $(CROSS_PREFIX)$(_CXX)$(HOST_EXE) $(ARCH_FLAGS)
else ifeq ($(COMPILER_NAME),vc)
# file extensions
A = .lib
B = $(SUFF64).exe
O = .obj
II = .i
# toolchain output switches
OBJSW = /Fo
OUTAR = /OUT:
OUTII = /Fi
OUTSW = /OUT:
OUTMAP = /map:
# misc switches
CPPONLY = /P
FORCEC = /TC
NOLOGO = /nologo
NORTTI = /GR-
OUTDLL = /DLL
# utilities
AR = $(MSVC_BIN)/lib.exe $(NOLOGO)
CC = $(MSVC_BIN)/cl.exe $(NOLOGO)
CCL = $(MSVC_BIN)/link.exe $(NOLOGO)
CXX = $(CC)
endif
##############################################################################
# target-specific cflags/ldflags
ifneq (,$(filter $(COMPILER_NAME),gcc clang))
# system cflags
CC_DEFS += $(PROCDEF)
ifdef __MAC__
CC_DEFS += __MAC__
else
CC_DEFS += __LINUX__
endif
ifdef __APPLE_SILICON__
CC_DEFS += __APPLE_SILICON__
endif
# pic-related flags
# Note: this variable may be overridden in other parts of the build
PIC = -fPIC
ifdef __MAC__
LDPIE = $(PIC) -Wl,-pie
else
LDPIE = $(PIC) -pie
endif
# common cflags
CC_DEFS += $(DEF64)
CC_DEFS += $(DEFX86)
CC_F += $(PIC)
CC_F += -fdiagnostics-show-option
CC_F += -fno-strict-aliasing
CC_F += -fvisibility=hidden
CC_F += -fwrapv
ifneq ($(COMPILER_NAME),clang)
CC_F += -fno-delete-null-pointer-checks
endif
CC_INCP += $(I)
CC_W += -Wall
CC_W += -Wextra
CC_W += -Wformat=2
CC_W += -Werror=format-security
CC_W += -Werror=format-nonliteral
CC_W += -Wshadow
CC_W += -Wunused
CC_WNO += -Wno-format-y2k
CC_WNO += -Wno-missing-field-initializers
CC_WNO += -Wno-sign-compare
CC_X += -g
CC_X += -pipe
# enable c++11
CXXSTD = -std=c++11
CXX_F += -fvisibility-inlines-hidden
CXX_WNO += -Wno-invalid-offsetof
# system-specific cflags
ifeq ($(COMPILER_NAME),clang) # mac/android
# 'cc -dumpversion' always reports 4.2.1 for clang
# https://stackoverflow.com/questions/12893731/why-does-clang-dumpversion-report-4-2-1
# clang is extra picky - need to add some warning supressions
# must eventually get rid of most of these
CC_WNO += -Wno-char-subscripts
CC_WNO += -Wno-dynamic-class-memaccess
CC_WNO += -Wno-int-to-pointer-cast
CC_WNO += -Wno-invalid-source-encoding
CC_WNO += -Wno-logical-not-parentheses
CC_WNO += -Wno-logical-op-parentheses
CC_WNO += -Wno-null-conversion
CC_WNO += -Wno-nullability-completeness
CC_WNO += -Wno-parentheses-equality
CC_WNO += -Wno-self-assign
CC_WNO += -Wno-unused-const-variable
CC_WNO += -Wno-unused-function
CC_WNO += -Wno-unused-private-field
CC_WNO += -Wno-unused-variable
CC_WNO += -Wno-varargs
CC_F += -fno-caret-diagnostics
else # (arm)linux
# get gcc version
ifndef _GCC_VERSION
_GCC_VERSION:=$(wordlist 1,2,$(subst ., ,$(shell $(CC) -dumpversion)))
export _GCC_VERSION
endif
GCC_VERSION=$(firstword $(_GCC_VERSION)).$(lastword $(_GCC_VERSION))
CC_WNO += -Wno-unused-local-typedefs
CC_F += -fno-diagnostics-show-caret
CC_DEFS-$(call gte,$(GCC_VERSION),5.0) += _GLIBCXX_USE_CXX11_ABI=0
CC_W-$(call gte,$(GCC_VERSION),7.0) += -Wimplicit-fallthrough=0
CXX_WNO-$(call gte,$(GCC_VERSION),8.0) += -Wno-class-memaccess
# suppress warning about ABI change in GCC 4.4
CC_WNO-$(__ARMLINUX__) += -Wno-psabi
endif
# optimization cflags
ifdef NDEBUG
CC_F += -fdata-sections
CC_F += -ffunction-sections
ifndef __ASAN__
CC_F += -fomit-frame-pointer
endif
# stack protector
ifdef __TARGET_MAC_HOST_LINUX__
# disable stack protector for our osxcross toolchain (we check
# against __TARGET_MAC_HOST_LINUX__ since it is hard to check
# for version number in clang).
else ifeq ($(call gte,$(GCC_VERSION),4.9),1)
CC_F += -fstack-protector-strong
else
CC_F += -fstack-protector
endif
CC_DEFS += NDEBUG
CC_DEFS += _FORTIFY_SOURCE=2
else
CC_DEFS += _DEBUG
endif
# system-specific ldflags
ifdef __LINUX__
LDFLAGS += -Wl,--build-id
LDFLAGS += -Wl,--gc-sections
LDFLAGS += -Wl,--warn-shared-textrel
NO_UNDEFS = -Wl,--no-undefined
DLL_W += $(NO_UNDEFS)
else ifdef __MAC__
LDFLAGS += -Wl,-dead_strip
ifndef __TARGET_MAC_HOST_LINUX__
DLL_X += -compatibility_version 1.0
DLL_X += -current_version 1.0
DLL_X += -single_module
endif
endif
# common linker/compiler flags
ifdef NDEBUG
CCOPT += -O2
ifdef __LINUX__
LDOPT += -Wl,-O1
endif
endif
# AddressSanitizer flags
ifdef __ASAN__
CC_DEFS += __ASAN__
CC_F += -fno-omit-frame-pointer
CC_F += -fsanitize=address
LDFLAGS += -fsanitize=address
export LSAN_OPTIONS=suppressions=$(IDA)etc/bin/known_leaks.txt:detect_leaks=0
endif
# final compiler flags
CC_F += $(CC_F-1)
CC_W += $(CC_W-1)
CC_WNO += $(CC_WNO-1)
CXX_WNO += $(CXX_WNO-1)
CC_DEFS += $(CC_DEFS-1)
CC_INCP += $(CC_INCP-1)
CC_D += $(addprefix -D,$(CC_DEFS))
CC_I += $(addprefix -I,$(CC_INCP))
# the -Wno-* flags must come after the -W enabling flags
WARNS = $(sort $(CC_W)) $(sort $(CC_WNO))
CFLAGS += $(sort $(CC_X))
CFLAGS += $(CCOPT)
CFLAGS += $(sort $(CC_I))
CFLAGS += $(sort $(CC_D))
CFLAGS += $(sort $(CC_F))
CFLAGS += $(WARNS)
CFLAGS += $(PTHR_SWITCH)
# for warning suppression, override the WARNS variable with NOWARNS:
# $(TARGET): WARNS = $(NOWARNS)
NOWARNS = -w
# dll linker flags
DLLFLAGS += $(DLL_W) $(DLL_X)
else ifeq ($(COMPILER_NAME),vc)
# for warning suppression, override the WARNS variable with NOWARNS:
# $(TARGET): WARNS = $(NOWARNS)
NOWARNS = -w -wd4702 -wd4738
# optimization ldflags
LDOPT += /DEBUG
ifdef NDEBUG
LDOPT += /INCREMENTAL:NO /OPT:ICF /OPT:REF
endif
# set c runtime to use
ifdef NDEBUG
ifdef USE_STATIC_RUNTIME
RUNTIME_LIBSW = /MT
else
RUNTIME_LIBSW = /MD
endif
else
ifdef USE_STATIC_RUNTIME
RUNTIME_LIBSW = /MTd
else
RUNTIME_LIBSW = /MDd
endif
endif
# PDB options
PDBFLAGS = /PDB:$(PDBDIR)/
ifdef NDEBUG
PDBFLAGS += /PDBALTPATH:%_PDB%
endif
# Generate debug info (old style)
PDBFORMAT = /Z7
# final compiler flags
CC_DEFS += $(DEF64)
CC_DEFS += $(DEFX86)
CC_DEFS += $(CC_DEFS-1)
CC_INCP += $(CC_INCP-1)
CC_D += $(addprefix -D,$(CC_DEFS))
CC_I += $(addprefix -I,$(CC_INCP))
CFGFILE = @$(IDA)$(SYSDIR).cfg
CFLAGS += $(CFGFILE)
CFLAGS += $(RUNTIME_LIBSW)
CFLAGS += $(PDBFORMAT)
CFLAGS += /Brepro
CFLAGS += $(sort $(CC_I))
CFLAGS += $(sort $(CC_D))
CFLAGS += $(sort $(CC_F))
CFLAGS += $(WARNS)
# final linker flags
LDFLAGS += /Brepro
LDFLAGS += $(PDBFLAGS)
LDFLAGS += /ERRORREPORT:QUEUE
ifdef __X86__
LDFLAGS += /LARGEADDRESSAWARE
endif
ifdef __XPCOMPAT__
XPSUBSYS-$(__X64__) = /SUBSYSTEM:CONSOLE,5.02
XPSUBSYS-$(__X86__) = /SUBSYSTEM:CONSOLE,5.01
LDFLAGS += $(XPSUBSYS-1)
endif
endif
# to enable obsolete functions, disable the NO_OBSOLETE_FUNCS variable:
# $(TARGET): NO_OBSOLETE_FUNCS =
NO_OBSOLETE_FUNCS = NO_OBSOLETE_FUNCS
CC_DEFS += $(NO_OBSOLETE_FUNCS)
CXXFLAGS += $(CXXSTD)
CXXFLAGS += $(CFLAGS)
CXXFLAGS += $(sort $(CXX_F))
CXXFLAGS += $(sort $(CXX_WNO))
CXXFLAGS += $(UFLAGS)
LDFLAGS += $(LDOPT)
#############################################################################
ifdef __X86__
DEFX86 = __X86__
endif
ifdef __EA64__
SUFF64=64
ADRSIZE=64
DEF64 = __EA64__
else
ADRSIZE=32
endif
ifdef NDEBUG
OPTSUF=_opt
endif
ifdef IDAHOME
EXTRASUF1:=_home
IDAHOME_PROCESSORS=pc arm ppc mips mc68k
else ifdef DEMO_OR_FREE
ifdef DEMO
EXTRASUF1:=_demo
else
EXTRASUF1:=_free
endif
IDADEMO_PROCESSOR=pc
IDADEMO_LOADERS=pe elf mach-o
else
ifdef USE_STATIC_RUNTIME
EXTRASUF1:=_s
endif
ifdef __ASAN__
EXTRASUF2:=_asan
endif
ifdef __FUZZER__
EXTRASUF3:=_afl
endif
endif
EXTRASUF=$(EXTRASUF1)$(EXTRASUF2)$(EXTRASUF3)
#############################################################################
SYSDIR=$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(OPTSUF)$(EXTRASUF)
# libraries directory
LIBDIR=$(IDA)lib/$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(EXTRASUF)
# object files directory (using ?= to allow overriding)
OBJDIR?=obj/$(SYSDIR)
# PDB files directory
PDBDIR=$(IDA)pdb/$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(EXTRASUF)
# output directory for target platform
R=$(IDA)bin/
# input directory with existing build utilities
RS=$(IDA)bin/
# _ida.hlp placed in main tool directory
HI=$(RS)
# help source
HS=.hls
# help headers
HH=.hhp
# include,help and other directories are common for all platforms and compilers:
I =$(IDA)include/
C =$(R)cfg/
RI=$(R)idc/
F=$(OBJDIR)/
L=$(LIBDIR)/
DUMB=$(L)dumb$(O)
HELP=$(L)help$(O)
HLIB=$(HI)_ida.hlp
# to be used like this:
# $(L)va$(A): $(call lib, $(VA_OBJS))
lib=$(1); $(strip $(QARf)$(AR) $(OUTAR)$$@ $$^)
# to be used like this: $(call _link_exe, target, objs, libs)
_link_exe=$(strip $(QCCL)$(CCL) $(OUTSW)$(1) $(2) $(3) $(LDFLAGS) $(STDLIBS))
# to be used like this: $(call link_exe, objs, libs)
link_exe=$(call _link_exe,$@,$(1),$(2))
# to be used like this: $(call _link_dll, target, objs, libs)
_link_dll=$(strip $(QCCL)$(CCL) $(OUTDLL) $(DLLFLAGS) $(OUTSW)$(1) $(2) $(3) $(LDFLAGS) $(STDLIBS))
# to be used like this: $(call link_dll, objs, libs)
link_dll=$(call _link_dll,$@,$(1),$(2))
# to be used like this: $(call link_dumb, target, libs, objs)
link_dumb=$(3) $(patsubst %,$(L)%$(A),$(2)); $(strip $(QCCLf)$(CCL) $(OUTSW)$(1) $(LDFLAGS) $(3) $(patsubst %,$(L)%$(A),$(2)) $(STDLIBS))
# to be used like this:
# target: $(call dumb_target, libs, objs) extra_ldflags
dumb_target=$(call link_dumb,$$@,$(1),$(2) $(DUMB))
# to be used like this:
# $(R)%$(B): $(F)%$(O) $(call dumb_pattern, libs, objs) extra_ldflags
dumb_pattern=$(call link_dumb,$$@ $$<,$(1),$(2) $(DUMB))
# to be used like this:
# OBJS += $(call objs,obj1 obj2 obj3 ...)
objs=$(addprefix $(F),$(addsuffix $(O),$(1)))
# output name for module dll
module_dll=$(BIN_PATH)$(1)$(SUFF64)$(DLLEXT)
# output name for server executable
server_exe=$(R)dbgsrv/$(1)
ifeq ($(or $(M),$(MM),$(MMH),$(MO),$(MMO),$(MMHO)),1)
BUILD_IDA = 1
endif
ifeq ($(or $(M32),$(MM),$(MO32),$(MMO)),1)
BUILD_DBGSRV = 1
endif
# target-os specific variables
ifdef __NT__
DLLEXT=.dll
else ifdef __MAC__
DLLEXT=.dylib
else
DLLEXT=.so
endif
# build system commands
ifeq ($(OS),Windows_NT)
CP=cp -f --preserve=all
MKDIR=-@mkdir
AWK=gawk
else
CP=cp -f
MKDIR=-@mkdir 2>/dev/null
AWK=awk
endif
RM=rm -f
MV=mv
# used to silence some makefile commands
# run 'make Q=' to prevent commands from being silenced
Q?=@
# some makefiles rebuild targets when the makefile itself changes.
# this makes debugging makefiles a pain.
# run 'make MAKEFILE_DEP=' to disable this behaviour.
MAKEFILE_DEP?=makefile
# libida-related
# Note: $(IDALIB) should be used in the dependency list
# $(LINKIDA) should be used in the link command
ifdef __NT__
# Note: on Windows, ida.lib does not have a "64" suffix for ea64
IDALIB = $(L)ida$(A)
LINKIDA = $(IDALIB)
else
IDALIB = $(L)libida$(SUFF64)$(DLLEXT)
LINKIDA = -L$(L) -lida$(SUFF64)
endif
# simplify command echo
ifdef IDAMAKE_SIMPLIFY
ifeq ($(Q),@)
DO_IDAMAKE_SIMPLIFY=1
endif
endif
ifdef DO_IDAMAKE_SIMPLIFY
ifdef IDAMAKE_SIMPLIFY_NO_COLOR
qcolor=$(1)
else
ifeq ($(OS),Windows_NT)
qcolor=-e #
endif
qcolor+="\033[1;34m$(1)\033[0m"
endif
QCXX = @echo $(call qcolor,compile) $< && #
QCC = @echo $(call qcolor,compile) $< && #
QASM = @echo $(call qcolor,asm) $< && #
QARf = @echo $(call qcolor,lib) $$@ && #
QCCL = @echo $(call qcolor,link) $@ && #
QCCLf = @echo $(call qcolor,link) $$@ && #
endif
# simple build rules
CONLY?=-c
$(F)%$(O): %.cpp
$(strip $(QCXX)$(CXX) $(CXXFLAGS) $(NORTTI) $(CONLY) $(OBJSW)$@ $<)
$(F)%$(O): %.c
$(strip $(QCC)$(CC) $(CFLAGS) $(CONLY) $(OBJSW)$@ $(FORCEC) $<)
$(C)%.cfg: %.cfg
$(CP) $? $@
# http://www.cmcrossroads.com/article/printing-value-makefile-variable
print-%:
@echo $* = "$($*)"
@echo $*\'s origin is $(origin $*)
#############################################################################
.PHONY: all test cfg includes
# Force make to delete the target if the rule to build it fails
.DELETE_ON_ERROR:

363
idasdk76/dbg/arm_debmod.cpp Normal file
View File

@@ -0,0 +1,363 @@
#include <pro.h>
#include <nalt.hpp>
#include "arm_debmod.h"
#ifdef ENABLE_LOWCNDS
inline bool has_armv5(void) { return true; }
static arm_debmod_t *ssmod; // pointer to the current debugger module
#endif
//--------------------------------------------------------------------------
arm_debmod_t::arm_debmod_t(void)
{
static const uchar bpt[] = ARM_BPT_CODE;
bpt_code.append(bpt, sizeof(bpt));
sp_idx = R_SP;
pc_idx = R_PC;
lr_idx = R_LR;
sr_idx = R_PSR;
nregs = qnumber(arm_registers);
set_platform("linux");
reset_hwbpts();
}
//--------------------------------------------------------------------------
int arm_debmod_t::getn_hwbpts_supported(void)
{
if ( hwbpt_count < 0 )
{
// unknown, ask the subclass how many hwbpts are supported
hwbpt_count = _getn_hwbpts_supported();
if ( hwbpt_count < 0 )
hwbpt_count = 0;
else
hwbpt_count = qmin(hwbpt_count, ARM_MAX_HWBPTS);
}
return hwbpt_count;
}
//--------------------------------------------------------------------------
int arm_debmod_t::getn_watchpoints_supported(void)
{
if ( watch_count < 0 )
{
// unknown, ask the subclass how many watchpoints are supported
watch_count = _getn_watchpoints_supported();
if ( watch_count < 0 )
watch_count = 0;
else
watch_count = qmin(watch_count, ARM_MAX_WATCHPOINTS);
}
return watch_count;
}
//--------------------------------------------------------------------------
int arm_debmod_t::find_hwbpt_slot(int *out, ea_t ea)
{
int slot = -1;
for ( int i = 0, n = getn_hwbpts_supported(); i < n; i++ )
{
if ( hwbpt_slots[i].addr == ea )
return BPT_BAD_ADDR; // duplicate
if ( hwbpt_slots[i].addr == BADADDR && slot < 0 )
slot = i;
}
if ( slot < 0 )
return BPT_TOO_MANY;
if ( out != nullptr )
*out = slot;
return BPT_OK;
}
//--------------------------------------------------------------------------
int arm_debmod_t::find_watchpoint_slot(int *out, ea_t ea)
{
int slot = -1;
for ( int i = 0, n = getn_watchpoints_supported(); i < n; i++ )
{
if ( watch_slots[i].addr == ea )
return BPT_BAD_ADDR; // duplicate
if ( watch_slots[i].addr == BADADDR && slot < 0 )
slot = i;
}
if ( slot < 0 )
return BPT_TOO_MANY;
if ( out != nullptr )
*out = slot;
return BPT_OK;
}
//--------------------------------------------------------------------------
int idaapi arm_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t ea, int /*len*/)
{
switch ( type )
{
case BPT_SOFT:
break;
case BPT_EXEC:
{
if ( getn_hwbpts_supported() <= 0 )
return BPT_BAD_TYPE;
int code = find_hwbpt_slot(nullptr, ea);
if ( code != BPT_OK )
return code;
}
break;
default:
{
if ( getn_watchpoints_supported() <= 0 )
return BPT_BAD_TYPE;
int code = find_watchpoint_slot(nullptr, ea);
if ( code != BPT_OK )
return code;
}
break;
}
return BPT_OK;
}
//--------------------------------------------------------------------------
bool arm_debmod_t::add_hwbpt(bpttype_t type, ea_t ea, int len)
{
int slot;
bool ok = false;
switch ( type )
{
case BPT_EXEC:
if ( find_hwbpt_slot(&slot, ea) == BPT_OK )
{
hwbpt_slots[slot].type = type;
hwbpt_slots[slot].addr = ea;
hwbpt_slots[slot].ctrl = get_hwbpt_ctrl_bits(type, ea, len);
ok = true;
}
break;
default:
if ( find_watchpoint_slot(&slot, ea) == BPT_OK )
{
watch_slots[slot].type = type;
watch_slots[slot].addr = ea;
watch_slots[slot].ctrl = get_watchpoint_ctrl_bits(type, ea, len);
ok = true;
}
break;
}
return ok && refresh_hwbpts();
}
//--------------------------------------------------------------------------
bool arm_debmod_t::del_hwbpt(ea_t ea, bpttype_t type)
{
bool ok = false;
switch ( type )
{
case BPT_EXEC:
for ( int i = 0, n = getn_hwbpts_supported(); i < n; i++ )
{
if ( hwbpt_slots[i].addr == ea )
{
hwbpt_slots[i].clear();
ok = true;
break;
}
}
break;
default:
for ( int i = 0, n = getn_watchpoints_supported(); i < n; i++ )
{
if ( watch_slots[i].addr == ea )
{
watch_slots[i].clear();
ok = true;
break;
}
}
break;
}
return ok && refresh_hwbpts();
}
//--------------------------------------------------------------------------
void arm_debmod_t::reset_hwbpts(void)
{
hwbpt_count = -1;
watch_count = -1;
for ( size_t i = 0; i < ARM_MAX_HWBPTS; i++ )
hwbpt_slots[i].clear();
for ( size_t i = 0; i < ARM_MAX_WATCHPOINTS; i++ )
watch_slots[i].clear();
}
//--------------------------------------------------------------------------
void arm_debmod_t::cleanup_hwbpts(void)
{
reset_hwbpts();
refresh_hwbpts();
}
//--------------------------------------------------------------------------
int arm_debmod_t::finalize_appcall_stack(
call_context_t &ctx,
regval_map_t &regs,
bytevec_t &/*stk*/)
{
regs[lr_idx].ival = ctx.ctrl_ea;
// return addrsize as the adjustment factor to add to sp
// we do not need the return address, that's why we ignore the first 4
// bytes of the prepared stack image
return debapp_attrs.addrsize;
}
//--------------------------------------------------------------------------
int arm_debmod_t::get_regidx(const char *regname, int *clsmask)
{
return arm_get_regidx(clsmask, regname);
}
#ifdef ENABLE_LOWCNDS
//--------------------------------------------------------------------------
static const regval_t &idaapi arm_getreg(const char *name, const regval_t *regvals)
{
int idx = ssmod->get_regidx(name, NULL);
QASSERT(30182, idx >= 0 && idx < ssmod->nregs);
return regvals[idx];
}
//--------------------------------------------------------------------------
static uint32 idaapi arm_get_long(ea_t ea)
{
uint32 v = -1;
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
return v;
}
//--------------------------------------------------------------------------
static uint16 idaapi arm_get_word(ea_t ea)
{
uint16 v = -1;
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
return v;
}
//--------------------------------------------------------------------------
static uint8 idaapi arm_get_byte(ea_t ea)
{
uint8 v = -1;
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
return v;
}
//----------------------------------------------------------------------
// stripped down version of get_dtype_size()
static size_t idaapi arm_get_dtype_size(op_dtype_t dtype)
{
switch ( dtype )
{
case dt_byte: return 1; // 8 bit
case dt_word:
case dt_half: return 2; // 16 bit
case dt_dword:
case dt_float: return 4; // 4 byte
case dt_qword:
case dt_double: return 8; // 8 byte
default: return 0;
}
}
//--------------------------------------------------------------------------
// since arm does not have a single step facility, we have to emulate it
// with a temporary breakpoint.
drc_t arm_debmod_t::dbg_perform_single_step(debug_event_t *dev, const insn_t &insn)
{
// read register values
regvals_t values;
values.resize(nregs);
drc_t drc = dbg_read_registers(dev->tid, ARM_RC_GENERAL, values.begin(), NULL);
if ( drc <= DRC_NONE )
return drc;
static const opinfo_helpers_t oh =
{
arm_getreg,
arm_get_byte,
arm_get_word,
arm_get_long,
arm_get_dtype_size,
NULL, // has_insn_cf_chg not needed
};
// calculate the address of the next executed instruction
lock_begin();
ssmod = this;
ea_t next = calc_next_exec_insn(insn, values.begin(), oh, false); // TODO pass is_mprofile parameter
ssmod = NULL;
lock_end();
// BADADDR means that the execution flow is linear
if ( next == BADADDR )
{
next = insn.ea + insn.size;
if ( (values[sr_idx].ival & BIT5) != 0 ) // thumb?
next |= 1;
}
// safety check: self jumping instruction cannot be single stepped
if ( (next & ~1) == insn.ea )
return DRC_FAILED;
// add a breakpoint there
update_bpt_info_t ubi;
ubi.ea = next;
ubi.type = BPT_SOFT;
ubi.code = 0;
int nbpts;
drc = dbg_update_bpts(&nbpts, &ubi, 1, 0, NULL);
if ( drc != DRC_OK || nbpts == 0 )
return drc != DRC_OK ? drc : DRC_FAILED;
drc = resume_app_and_get_event(dev);
// clean up: delete the temporary breakpoint
ubi.ea &= ~1; // del_bpt requires an even address
drc_t drc2 = dbg_update_bpts(&nbpts, &ubi, 0, 1, NULL);
if ( drc2 != DRC_OK || nbpts == 0 )
{
msg("%a: failed to remove single step bpt?!\n", ubi.ea);
drc = drc2 != DRC_OK ? drc2 : DRC_FAILED;
}
// the caller expects to see STEP after us:
if ( drc == DRC_OK )
dev->set_eid(STEP);
return drc;
}
#endif // ENABLE_LOWCNDS
//--------------------------------------------------------------------------
void arm_debmod_t::adjust_swbpt(ea_t *p_ea, int *p_len)
{
ea_t &ea = *p_ea;
if ( (ea & 1) != 0 ) // T bit is set, use a thumb breakpoint
{
ea--;
*p_len = 2;
}
}

77
idasdk76/dbg/arm_debmod.h Normal file
View File

@@ -0,0 +1,77 @@
#ifndef __ARM_DEBMOD__
#define __ARM_DEBMOD__
#include "arm_regs.hpp"
#include "deb_arm.hpp"
#include "debmod.h"
//--------------------------------------------------------------------------
struct arm_hwbpt_info_t
{
bpttype_t type; // BPT_...
ea_t addr; // target address
ea_t ctrl; // control value
arm_hwbpt_info_t(void) { clear(); }
void clear(void)
{
type = BPT_DEFAULT;
addr = BADADDR;
ctrl = 0;
}
};
//--------------------------------------------------------------------------
class arm_debmod_t : public debmod_t
{
typedef debmod_t inherited;
protected:
int hwbpt_count;
int watch_count;
arm_hwbpt_info_t hwbpt_slots[ARM_MAX_HWBPTS];
arm_hwbpt_info_t watch_slots[ARM_MAX_WATCHPOINTS];
int lr_idx;
int sr_idx;
public:
arm_debmod_t(void);
void reset_hwbpts(void);
void cleanup_hwbpts(void);
int getn_hwbpts_supported(void);
int getn_watchpoints_supported(void);
int find_hwbpt_slot(int *slot, ea_t ea);
int find_watchpoint_slot(int *slot, ea_t ea);
bool add_hwbpt(bpttype_t type, ea_t ea, int len);
bool del_hwbpt(ea_t ea, bpttype_t type);
// subclasses must implement these functions to properly support hardware breakpoints/watchpoints
virtual int _getn_hwbpts_supported(void) newapi { return -1; }
virtual int _getn_watchpoints_supported(void) newapi { return -1; }
virtual ea_t get_hwbpt_ctrl_bits(bpttype_t, ea_t, int) newapi { return 0; }
virtual ea_t get_watchpoint_ctrl_bits(bpttype_t, ea_t, int) newapi { return 0; }
virtual bool refresh_hwbpts(void) newapi { return false; }
// overridden base class functions
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) override;
virtual int finalize_appcall_stack(call_context_t &ctx, regval_map_t &regs, bytevec_t &stk) override;
virtual int get_regidx(const char *regname, int *clsmask) override;
virtual void adjust_swbpt(ea_t *p_ea, int *p_len) override;
protected:
#ifdef ENABLE_LOWCNDS
virtual drc_t dbg_perform_single_step(debug_event_t *dev, const insn_t &insn) override;
#endif
};
bool is_32bit_thumb_insn(uint16 code);
#endif

View File

@@ -0,0 +1,101 @@
#include <idp.hpp>
#include <dbg.hpp>
#include <loader.hpp>
#include <segregs.hpp>
#include <segment.hpp>
#include "deb_arm.hpp"
#include "arm_regs.cpp"
//--------------------------------------------------------------------------
int is_arm_valid_bpt(bpttype_t type, ea_t ea, int len)
{
switch ( type )
{
case BPT_SOFT:
if ( (ea & 1) != 0 )
return BPT_BAD_ADDR;
break;
case BPT_EXEC:
if ( (ea & 3) != 0 )
return BPT_BAD_ALIGN;
break;
default:
if ( (ea & 7) != 0 )
return BPT_BAD_ALIGN;
if ( len < 1 || len > 8 )
return BPT_BAD_LEN;
break;
}
return BPT_OK;
}
//--------------------------------------------------------------------------
// if bit0 is set, ensure that thumb mode
// if bit0 is clear, ensure that arm mode
static void handle_arm_thumb_modes(ea_t ea)
{
bool should_be_thumb = (ea & 1) != 0;
bool is_thumb = processor_t::get_code16_mode(ea);
if ( should_be_thumb != is_thumb )
processor_t::set_code16_mode(ea, should_be_thumb);
}
//--------------------------------------------------------------------------
static easet_t pending_addresses;
static ssize_t idaapi dbg_callback(void *, int code, va_list)
{
// we apply thumb/arm switches when the process is suspended.
// it is quite late (normally we should do it as soon as the corresponding
// segment is created) but i did not manage to make it work.
// in the segm_added event the addresses are not enabled yet,
// so switching modes fails.
if ( code == dbg_suspend_process && !pending_addresses.empty() )
{
for ( easet_t::iterator p=pending_addresses.begin();
p != pending_addresses.end();
++p )
{
handle_arm_thumb_modes(*p);
}
pending_addresses.clear();
}
return 0;
}
//--------------------------------------------------------------------------
// For ARM processors the low bit means 1-thumb, 0-arm mode.
// The following function goes over the address list and sets the mode
// in IDA database according to bit0. It also resets bit0 for all addresses.
void set_arm_thumb_modes(ea_t *addrs, int qty)
{
for ( int i=0; i < qty; i++ )
{
ea_t ea = addrs[i];
segment_t *s = getseg(ea);
if ( s == NULL )
pending_addresses.insert(ea);
else
handle_arm_thumb_modes(ea);
addrs[i] = ea & ~1;
}
}
//--------------------------------------------------------------------------
void processor_specific_init(void)
{
hook_to_notification_point(HT_DBG, dbg_callback);
}
//--------------------------------------------------------------------------
void processor_specific_term(void)
{
unhook_from_notification_point(HT_DBG, dbg_callback);
pending_addresses.clear();
}

259
idasdk76/dbg/arm_regs.cpp Normal file
View File

@@ -0,0 +1,259 @@
#include "arm_regs.hpp"
//-------------------------------------------------------------------------
// NOTE: keep in sync with register_class_arm_t
const char *arm_register_classes[] =
{
"General registers",
"VFP registers",
"NEON registers",
NULL
};
#ifndef __EA64__
//-------------------------------------------------------------------------
static const char *const psr[] =
{
"MODE", // 0
"MODE", // 1
"MODE", // 2
"MODE", // 3
"MODE", // 4
"T", // 5
"F", // 6
"I", // 7
"A", // 8
"E", // 9
"IT", // 10
"IT", // 11
"IT", // 12
"IT", // 13
"IT", // 14
"IT", // 15
"GE", // 16
"GE", // 17
"GE", // 18
"GE", // 19
NULL, // 20
NULL, // 21
NULL, // 22
NULL, // 23
"J", // 24
"IT2", // 25 additional bits of IT
"IT2", // 26 additional bits of IT
"Q", // 27
"V", // 28
"C", // 29
"Z", // 30
"N", // 31
};
//-------------------------------------------------------------------------
static const char *const vfp_format[] =
{
"VFP_1_double",
};
#else
//-------------------------------------------------------------------------
static const char *const psr[] =
{
"M", // 0 AArch32 mode that an exception was taken from
"M", // 1
"M", // 2
"M", // 3
"M", // 4 Execution state that the exception was taken from
"T", // 5 T32 Instruction set state bit
"F", // 6 FIQ mask bit
"I", // 7 IRQ mask bit
"A", // 8 Asynchronous data abort mask bit
"E", // 9 Endianness Execution State bit
"IT", // 10 If-Then
"IT", // 11
"IT", // 12
"IT", // 13
"IT", // 14
"IT", // 15
"GE", // 16 Greater than or Equal flags
"GE", // 17
"GE", // 18
"GE", // 19
"IL", // 20 Illegal Execution State bit
NULL, // 21
NULL, // 22
NULL, // 23
NULL, // 24
"IT2", // 25 If-Then
"IT2", // 26
"Q", // 27 Cumulative saturation bit
"V", // 28 oVerflow condition flag
"C", // 29 Carry condition flag
"Z", // 30 Zero condition flag
"N", // 31 Negative condition flag
};
//-------------------------------------------------------------------------
static const char *const neon_formats[] =
{
"NEON_16_bytes",
"NEON_8_words",
"NEON_4_dwords",
"NEON_2_qwords",
"NEON_4_floats",
"NEON_2_doubles",
};
#endif
//-------------------------------------------------------------------------
// NOTE: keep in sync with register_arm_t
register_info_t arm_registers[] =
{
#ifndef __EA64__
// General registers
{ "R0", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R1", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R2", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R3", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R4", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R5", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R6", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R7", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R8", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R9", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R10", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R11", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "R12", REGISTER_ADDRESS|REGISTER_FP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "SP", REGISTER_ADDRESS|REGISTER_SP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "LR", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "PC", REGISTER_ADDRESS|REGISTER_IP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
{ "PSR", 0, ARM_RC_GENERAL, dt_dword, psr, 0xF800007F },
// VFP registers
{ "D0", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D1", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D2", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D3", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D4", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D5", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D6", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D7", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D8", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D9", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D10", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D11", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D12", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D13", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D14", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D15", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D16", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D17", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D18", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D19", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D20", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D21", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D22", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D23", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D24", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D25", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D26", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D27", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D28", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D29", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D30", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "D31", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
{ "FPSCR", 0, ARM_RC_VFP, dt_dword, NULL, 0 },
#else
// General registers
{ "X0", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X1", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X2", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X3", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X4", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X5", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X6", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X7", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X8", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X9", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X10", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X11", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X12", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X13", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X14", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X15", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X16", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X17", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X18", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X19", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X20", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X21", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X22", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X23", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X24", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X25", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X26", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X27", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X28", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X29", REGISTER_ADDRESS|REGISTER_FP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "X30", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "SP", REGISTER_ADDRESS|REGISTER_SP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "PC", REGISTER_ADDRESS|REGISTER_IP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
{ "PSR", 0, ARM_RC_GENERAL, dt_dword, psr, 0xF8000000 },
{ "V0", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V1", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V2", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V3", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V4", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V5", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V6", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V7", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V8", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V9", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V10", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V11", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V12", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V13", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V14", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V15", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V16", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V17", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V18", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V19", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V20", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V21", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V22", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V23", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V24", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V25", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V26", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V27", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V28", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V29", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V30", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "V31", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
{ "FPSR", 0, ARM_RC_NEON, dt_dword, NULL, 0 },
{ "FPCR", 0, ARM_RC_NEON, dt_dword, NULL, 0 },
#endif
};
CASSERT(qnumber(arm_registers) == ARM_NREGS);
//-------------------------------------------------------------------------
int arm_get_regidx(int *clsmask, const char *regname)
{
for ( size_t i = 0; i < qnumber(arm_registers); i++ )
{
if ( strieq(regname, arm_registers[i].name) )
{
if ( clsmask != NULL )
*clsmask = arm_registers[i].register_class;
return i;
}
}
return -1;
}
//-------------------------------------------------------------------------
int arm_get_regclass(int idx)
{
if ( idx >= 0 && idx < qnumber(arm_registers) )
return arm_registers[idx].register_class;
return 0;
}

182
idasdk76/dbg/arm_regs.hpp Normal file
View File

@@ -0,0 +1,182 @@
#pragma once
#include <pro.h>
#include <idd.hpp>
//-------------------------------------------------------------------------
#if defined(__LINUX__) && defined(__ARM__) && !defined(__EA64__)
# define __HAVE_ARM_VFP__
#endif
//-------------------------------------------------------------------------
#if defined(__MAC__) && defined(__ARM__)
# define __HAVE_ARM_NEON__
#endif
//-------------------------------------------------------------------------
// NOTE: keep in sync with arm_register_classes
enum register_class_arm_t
{
ARM_RC_GENERAL = 0x01, // General registers
ARM_RC_VFP = 0x02, // VFP registers
ARM_RC_NEON = 0x04, // NEON registers
ARM_RC_ALL = ARM_RC_GENERAL
#ifdef __HAVE_ARM_VFP__
| ARM_RC_VFP
#endif
#ifdef __HAVE_ARM_NEON__
| ARM_RC_NEON
#endif
};
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// NOTE: keep in sync with arm_registers
enum register_arm_t
{
#ifndef __EA64__
// General registers
R_R0,
R_R1,
R_R2,
R_R3,
R_R4,
R_R5,
R_R6,
R_R7,
R_R8,
R_R9,
R_R10,
R_R11,
R_R12,
R_SP,
R_LR,
R_PC,
R_PSR,
// VFP registers
R_D0,
R_D1,
R_D2,
R_D3,
R_D4,
R_D5,
R_D6,
R_D7,
R_D8,
R_D9,
R_D10,
R_D11,
R_D12,
R_D13,
R_D14,
R_D15,
R_D16,
R_D17,
R_D18,
R_D19,
R_D20,
R_D21,
R_D22,
R_D23,
R_D24,
R_D25,
R_D26,
R_D27,
R_D28,
R_D29,
R_D30,
R_D31,
R_FPSCR,
#else
// General registers
R_R0,
R_R1,
R_R2,
R_R3,
R_R4,
R_R5,
R_R6,
R_R7,
R_R8,
R_R9,
R_R10,
R_R11,
R_R12,
R_R13,
R_R14,
R_R15,
R_R16,
R_R17,
R_R18,
R_R19,
R_R20,
R_R21,
R_R22,
R_R23,
R_R24,
R_R25,
R_R26,
R_R27,
R_R28,
R_R29,
R_LR,
R_SP,
R_PC,
R_PSR,
// NEON registers
R_V0,
R_V1,
R_V2,
R_V3,
R_V4,
R_V5,
R_V6,
R_V7,
R_V8,
R_V9,
R_V10,
R_V11,
R_V12,
R_V13,
R_V14,
R_V15,
R_V16,
R_V17,
R_V18,
R_V19,
R_V20,
R_V21,
R_V22,
R_V23,
R_V24,
R_V25,
R_V26,
R_V27,
R_V28,
R_V29,
R_V30,
R_V31,
R_FPSR,
R_FPCR,
#endif
};
// Number of registers in arm and aarch64
#define ARM64_NREGS 68
#define ARM32_NREGS 50
#ifdef __EA64__
#define ARM_NREGS ARM64_NREGS
#else
#define ARM_NREGS ARM32_NREGS
#endif
//-------------------------------------------------------------------------
extern const char *arm_register_classes[];
extern register_info_t arm_registers[ARM_NREGS];
//-------------------------------------------------------------------------
int arm_get_regidx(int *clsmask, const char *regname);
int arm_get_regclass(int idx);

906
idasdk76/dbg/bin_search.cpp Normal file
View File

@@ -0,0 +1,906 @@
#include <pro.h>
#include "debmod.h"
//#define TEST
#ifdef TEST
static uchar memory[256];
static const int PAGESZ = 4;
static ssize_t read_page(ea_t ea, void *buf, size_t size, qstring *)
{
QASSERT(1517, (size % PAGESZ) == 0);
if ( ea >= sizeof(memory) )
return -1;
memcpy(buf, &memory[ea], size);
return size;
}
#else
static const int PAGESZ = 4096;
#define read_page(ea, buf, size, errbuf) mod->dbg_read_memory(ea, buf, size, errbuf)
#endif
static const int PAGE_HB = 1000; // page heartbeat counter
static const int TIME_HB = (RECV_TIMEOUT_PERIOD/1000) / 2;
// time period between heartbeats
//--------------------------------------------------------------------------
// memrchr is unavailable under Windows and MAC
#if defined(_WIN32) || defined(__MAC__)
// fixme: we need more optimized version
static void *local_memrchr(const void *s, int c, size_t n)
{
const unsigned char *start = (const unsigned char *)s;
const unsigned char *end = start + n - 1;
while ( end >= start )
{
if ( *end == c )
return (void *)end;
end--;
}
return NULL;
}
#else
#define local_memrchr memrchr
#endif
//--------------------------------------------------------------------------
class matcher_t
{
protected:
struct partmatch_t
{
ea_t match_ea; // starting address of the match
size_t ptn_idx; // index of the pattern
size_t ptn_off; // offset inside the pattern
};
typedef qlist<partmatch_t> partmatches_t;
// constructor arguments
ea_t *found_ea;
debmod_t *mod;
const compiled_binpat_vec_t &ptns;
int srch_flags;
qstring *errbuf; //lint !e958
uchar page[PAGESZ];
ea_t page_ea;
partmatches_t pmatches;
ea_t failed_ea;
// cache
intvec_t simple_ptns; // indexes of patterns w/o a mask and the search is case sensitive
intvec_t complex_ptns; // other patterns
// heartbeat
uint32 last_hb; // time in secs of the last heartbeat
matcher_t(
ea_t *_found_ea,
debmod_t *_mod,
const compiled_binpat_vec_t &_ptns,
int _srch_flags,
qstring *_errbuf)
: found_ea(_found_ea),
mod(_mod),
ptns(_ptns),
srch_flags(_srch_flags),
errbuf(_errbuf),
page_ea(BADADDR),
failed_ea(BADADDR)
{
for ( int i=0; i < ptns.size(); ++i )
{
const compiled_binpat_t &ptn = ptns[i];
if ( ptn.bytes.empty() )
continue;
if ( sense_case() && ptn.all_bytes_defined() ) // TODO: && !inf_is_wide_high_byte_first() - for servers
simple_ptns.push_back(i);
else
complex_ptns.push_back(i);
}
memset(page, 0, sizeof(page));
last_hb = get_secs(qtime64());
}
bool sense_case(void) const { return (srch_flags & BIN_SEARCH_CASE) != 0; }
bool check_break(void) const { return (srch_flags & BIN_SEARCH_NOBREAK) == 0; }
bool test_cancelled(void) const
{
struct ida_local tester_t : public exec_request_t
{
virtual ~tester_t() {}
virtual int idaapi execute(void)
{
return user_cancelled();
}
};
tester_t tester;
return static_cast<bool>(execute_sync(tester, MFF_FAST));
}
void send_heartbeat(size_t *page_counter)
{
*page_counter += 1;
if ( *page_counter >= PAGE_HB )
{
*page_counter = 0;
uint32 now = get_secs(qtime64());
if ( now - last_hb >= TIME_HB )
{
mod->dmsg(""); // heartbeat
last_hb = now;
}
}
}
public:
virtual ~matcher_t()
{
found_ea = NULL;
mod = NULL;
errbuf = NULL;
}
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) = 0;
drc_t search_memory_range(ea_t range_start_ea, ea_t range_end_ea)
{
if ( range_is_unreadable(range_start_ea) )
{
#ifndef TEST
mod->debdeb("dbg_bin_search memory range %a..%a is unreadable, skip it\n", range_start_ea, range_end_ea);
#endif
return DRC_FAILED;
}
return find(range_start_ea, range_end_ea);
}
virtual drc_t find(ea_t start_ea, ea_t end_ea) = 0;
bool match_pattern(
const uchar *page_ptr,
const compiled_binpat_t &ptn,
size_t ptn_off,
size_t nbytes) const
{
const uchar *image = ptn.bytes.begin() + ptn_off;
const uchar *mask = ptn.all_bytes_defined() ? NULL : ptn.mask.begin() + ptn_off;
for ( int i=0; i < nbytes; ++i, ++page_ptr, ++image )
{
uchar c = *page_ptr;
if ( mask != NULL )
{
if ( mask == SKIP_FF_MASK )
{
if ( *image == 0xFF )
continue;
}
else
{
if ( mask[i] == 0 )
continue;
}
}
uchar c2 = *image;
if ( sense_case() ? (c != c2) : (qtoupper(c) != qtoupper(c2)) )
return false;
}
return true;
}
ea_t get_failed_address(void) const { return failed_ea; }
private:
bool range_is_unreadable(ea_t range_start_ea)
{
uchar dummy;
return mod->dbg_read_memory(range_start_ea, &dummy, sizeof(dummy), NULL) != sizeof(dummy);
}
};
typedef janitor_t<matcher_t *> matcher_janitor_t;
template <> inline matcher_janitor_t::~janitor_t()
{
delete resource;
resource = NULL;
}
//--------------------------------------------------------------------------
class forward_matcher_t : public matcher_t
{
size_t last_off;
public:
forward_matcher_t(
ea_t *_found_ea,
debmod_t *_mod,
const compiled_binpat_vec_t &_ptns,
int _srch_flags,
qstring *_errbuf)
: matcher_t(_found_ea, _mod, _ptns, _srch_flags, _errbuf),
last_off(PAGESZ)
{}
//--------------------------------------------------------------------------
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) override
{
meminfo_vec_t::const_iterator p=mod->old_ranges.begin();
ea_t range_start_ea = BADADDR;
for ( ; p < mod->old_ranges.end(); ++p )
{
if ( p->contains(srch_start_ea) )
{
range_start_ea = srch_start_ea;
break;
}
if ( p->start_ea > srch_start_ea )
break;
}
if ( range_start_ea == BADADDR )
{
if ( p == mod->old_ranges.end() || p->start_ea >= srch_end_ea )
return DRC_FAILED; // not found
range_start_ea = p->start_ea;
}
ea_t range_end_ea = srch_end_ea < p->end_ea ? srch_end_ea : p->end_ea;
drc_t drc = search_memory_range(range_start_ea, range_end_ea);
if ( drc != DRC_FAILED ) // not found
return drc;
for ( ++p; p < mod->old_ranges.end() && srch_end_ea >= p->end_ea; ++p )
{
range_start_ea = p->start_ea;
range_end_ea = srch_end_ea < p->end_ea ? srch_end_ea : p->end_ea;
drc = search_memory_range(range_start_ea, range_end_ea);
if ( drc != DRC_FAILED ) // not found
return drc;
}
return DRC_FAILED; // not found
}
//--------------------------------------------------------------------------
// find patterns in [start_ea, end_ea)
virtual drc_t find(ea_t start_ea, ea_t end_ea) override
{
page_ea = align_down(start_ea, PAGESZ);
ea_t page_off = start_ea - page_ea;
size_t page_counter = 0;
while ( page_ea < end_ea )
{
if ( check_break() && test_cancelled() )
return DRC_ERROR;
if ( read_page(page_ea, page, sizeof(page), errbuf) != sizeof(page) )
{
failed_ea = page_ea;
return DRC_ERROR;
}
last_off = end_ea - page_ea;
if ( last_off > PAGESZ )
last_off = PAGESZ;
// handle partial matches first
for ( partmatches_t::iterator p=pmatches.begin(); p != pmatches.end(); )
{
switch ( finalize_partial_match(*p) )
{
case DRC_OK: // found a match
return DRC_OK;
default:
case DRC_FAILED: // definitely failed
p = pmatches.erase(p);
break;
case DRC_NONE: // need to continue matching
++p;
break;
}
}
// try to find new matches
if ( match_simple_patterns(page_off) )
return DRC_OK;
if ( !complex_ptns.empty() )
{
while ( page_off < last_off )
{
if ( match_at(page_off) )
return DRC_OK;
page_off++;
}
}
page_ea += PAGESZ; // advance to the next page
page_off = 0;
send_heartbeat(&page_counter);
}
return DRC_FAILED;
}
private:
//--------------------------------------------------------------------------
// try to match complex patterns at PAGE_OFF
// too long patterns that do not fit the page will be matched partially
// if the partial match is ok, we will remember them
bool match_at(ea_t page_off)
{
const uchar *page_ptr = page + page_off;
size_t rest = last_off - page_off;
for ( intvec_t::const_iterator p=complex_ptns.begin();
p != complex_ptns.end();
++p )
{
const int &i = *p;
const compiled_binpat_t &ptn = ptns[i];
size_t vecsize = ptn.bytes.size();
size_t nbytes = qmin(rest, vecsize);
if ( !match_pattern(page_ptr, ptn, 0, nbytes) )
continue;
if ( vecsize <= rest )
{
*found_ea = page_ea + page_off;
return true; // fits the page, a simple comparison is enough
}
// remember partial match
partmatch_t pm;
pm.match_ea = page_ea + page_off;
pm.ptn_idx = i;
pm.ptn_off = nbytes;
pmatches.push_back(pm);
}
return false;
}
//--------------------------------------------------------------------------
// try to match simple patterns inside the page
// the partial match is processed as described above
bool match_simple_patterns(ea_t page_off)
{
for ( intvec_t::const_iterator p=simple_ptns.begin();
p != simple_ptns.end();
++p )
{
const int &i = *p;
const uchar *page_ptr = page + page_off;
size_t rest = last_off - page_off;
const bytevec_t &ptn_bytes = ptns[i].bytes;
size_t ptn_sz = ptn_bytes.size();
uchar ptn_ch = ptn_bytes[0];
const uchar *pold = page_ptr;
while ( rest > 0 )
{
const uchar *pnew = (uchar *)memchr(pold, ptn_ch, rest);
if ( pnew == NULL )
break;
rest -= (pnew - pold);
size_t nbytes = qmin(rest, ptn_sz);
if ( memcmp(pnew, ptn_bytes.begin(), nbytes) == 0 )
{
ea_t matched_ea = page_ea + (pnew - page);
if ( ptn_sz <= rest )
{
*found_ea = matched_ea;
return true;
}
// remember partial match
partmatch_t pm;
pm.match_ea = matched_ea;
pm.ptn_idx = i;
pm.ptn_off = nbytes;
pmatches.push_back(pm);
}
pold = pnew + 1;
rest -= 1;
}
}
return false;
}
//--------------------------------------------------------------------------
// try to finalize a partial match by matching the next part of the
// long pattern against the start of the PAGE. patterns that are still
// too long for matching may produce new partial matches.
drc_t finalize_partial_match(partmatch_t &pm)
{
const compiled_binpat_t &ptn = ptns[pm.ptn_idx];
size_t vecsize = ptn.bytes.size();
size_t ptn_rest = vecsize - pm.ptn_off;
size_t nbytes = qmin(ptn_rest, last_off);
if ( !match_pattern(page, ptn, pm.ptn_off, nbytes) )
return DRC_FAILED;
if ( ptn_rest <= last_off )
{
*found_ea = pm.match_ea;
return DRC_OK; // finalized the match
}
if ( last_off != PAGESZ )
return DRC_FAILED;
// remember a new partial match
pm.ptn_off += PAGESZ;
return DRC_NONE;
}
};
//--------------------------------------------------------------------------
class backward_matcher_t : public matcher_t
{
ea_t page_off;
public:
backward_matcher_t(
ea_t *_found_ea,
debmod_t *_mod,
const compiled_binpat_vec_t &_ptns,
int _srch_flags,
qstring *_errbuf)
: matcher_t(_found_ea, _mod, _ptns, _srch_flags, _errbuf),
page_off(0)
{}
//--------------------------------------------------------------------------
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) override
{
meminfo_vec_t::const_iterator p=mod->old_ranges.end() - 1;
ea_t range_end_ea = BADADDR;
for ( ; p >= mod->old_ranges.begin(); --p )
{
if ( p->start_ea < srch_end_ea )
{
range_end_ea = srch_end_ea < p->end_ea ? srch_end_ea : p->end_ea;
break;
}
}
if ( range_end_ea == BADADDR )
return DRC_FAILED; // not found
ea_t range_start_ea = p->contains(srch_start_ea) ? srch_start_ea : p->start_ea;
drc_t drc = search_memory_range(range_start_ea, range_end_ea);
if ( drc != DRC_FAILED ) // not found
return drc;
for ( --p; p >= mod->old_ranges.begin() && srch_start_ea < p->end_ea; --p )
{
range_end_ea = p->end_ea;
range_start_ea = p->contains(srch_start_ea) ? srch_start_ea : p->start_ea;
drc = search_memory_range(range_start_ea, range_end_ea);
if ( drc != DRC_FAILED ) // not found
return drc;
}
return DRC_FAILED; // not found
}
//--------------------------------------------------------------------------
// find patterns in [start_ea, end_ea)
virtual drc_t find(ea_t start_ea, ea_t end_ea) override
{
page_ea = align_down(end_ea - 1, PAGESZ);
ea_t last_off = end_ea - page_ea;
size_t page_counter = 0;
while ( start_ea < page_ea + PAGESZ )
{
if ( check_break() && test_cancelled() )
return DRC_ERROR;
if ( read_page(page_ea, page, sizeof(page), errbuf) != sizeof(page) )
{
failed_ea = page_ea;
return DRC_ERROR;
}
page_off = page_ea < start_ea ? start_ea - page_ea : 0;
// handle partial matches first
for ( partmatches_t::iterator p=pmatches.begin(); p != pmatches.end(); )
{
switch ( finalize_partial_match(*p) )
{
case DRC_OK: // found a match
return DRC_OK;
default:
case DRC_FAILED: // definitely failed
p = pmatches.erase(p);
break;
case DRC_NONE: // need to continue matching
++p;
break;
}
}
// try to find new matches
if ( match_simple_patterns(last_off) )
return DRC_OK;
if ( !complex_ptns.empty() )
{
while ( page_off < last_off )
{
if ( match_before(last_off) )
return DRC_OK;
last_off--;
}
}
page_ea -= PAGESZ; // advance to the next page
last_off = PAGESZ;
send_heartbeat(&page_counter);
}
return DRC_FAILED;
}
private:
//--------------------------------------------------------------------------
// try to match all patterns before LAST_OFF
// too long patterns that do not fit the page will be matched partially
// if the partial match is ok, we will remember them
bool match_before(ea_t last_off)
{
size_t rest = last_off - page_off;
for ( intvec_t::const_iterator p=complex_ptns.begin();
p != complex_ptns.end();
++p )
{
const int &i = *p;
const compiled_binpat_t &ptn = ptns[i];
size_t vecsize = ptn.bytes.size();
size_t nbytes = qmin(rest, vecsize);
if ( !match_pattern(page+last_off-nbytes, ptn, vecsize-nbytes, nbytes) )
continue;
if ( vecsize <= rest )
{
*found_ea = page_ea + last_off - nbytes;
return true; // fits the page, a simple comparison is enough
}
// remember partial match
partmatch_t pm;
pm.match_ea = page_ea + last_off - vecsize;
pm.ptn_idx = i;
pm.ptn_off = nbytes;
pmatches.push_back(pm);
}
return false;
}
//--------------------------------------------------------------------------
// try to match simple patterns inside the page
// the partial match is processed as described above
bool match_simple_patterns(ea_t last_off)
{
const uchar *page_ptr = page + page_off;
for ( intvec_t::const_iterator q=simple_ptns.begin();
q != simple_ptns.end();
++q )
{
const int &i = *q;
size_t rest = last_off - page_off;
const bytevec_t &ptn_bytes = ptns[i].bytes;
size_t ptn_sz = ptn_bytes.size();
uchar ptn_ch = ptn_bytes[ptn_sz-1];
while ( rest > 0 )
{
const uchar *p = (uchar *)local_memrchr(page_ptr, ptn_ch, rest);
if ( p == NULL )
break;
rest = p + 1 - page_ptr;
size_t nbytes = qmin(rest, ptn_sz);
if ( memcmp(p + 1 - nbytes, &ptn_bytes[ptn_sz - nbytes], nbytes) == 0 )
{
ea_t matched_ea = page_ea + (p + 1 - page) - ptn_sz;
if ( ptn_sz <= rest )
{
*found_ea = matched_ea;
return true;
}
// remember partial match
partmatch_t pm;
pm.match_ea = matched_ea;
pm.ptn_idx = i;
pm.ptn_off = nbytes;
pmatches.push_back(pm);
}
rest -= 1;
}
}
return false;
}
//--------------------------------------------------------------------------
// try to finalize a partial match by matching the previous part of the
// long pattern against the end of the PAGE. patterns that are still
// too long for matching may produce new partial matches.
drc_t finalize_partial_match(partmatch_t &pm)
{
const compiled_binpat_t &ptn = ptns[pm.ptn_idx];
size_t vecsize = ptn.bytes.size();
size_t ptn_rest = vecsize - pm.ptn_off;
size_t nbytes = qmin(ptn_rest, PAGESZ - page_off);
if ( !match_pattern(page + PAGESZ - nbytes, ptn, ptn_rest - nbytes, nbytes) )
return DRC_FAILED;
if ( ptn_rest <= PAGESZ - page_off )
{
*found_ea = pm.match_ea;
return DRC_OK; // finalized the match
}
if ( page_off != 0 )
return DRC_FAILED;
// remember a new partial match
pm.ptn_off += PAGESZ;
return DRC_NONE;
}
};
#ifndef TEST
//--------------------------------------------------------------------------
// Note:
// The input search range can include the unreadable memory regions.
// For example, "[vvar]" on Linux.
// read_memory() returns 0 when trying to read from such region.
// These regions must be skipped.
drc_t idaapi debmod_t::dbg_bin_search(
ea_t *found_ea,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring *errbuf)
{
static int forbidden = -1;
if ( forbidden == -1 )
forbidden = qgetenv("IDA_IDB_BIN_SEARCH", NULL);
if ( forbidden )
return DRC_NONE;
debdeb("dbg_bin_search %a..%a\n", start_ea, end_ea);
if ( start_ea >= end_ea || ptns.empty() || old_ranges.empty() )
return DRC_NONE;
//lint -esym(429,matcher) has not been freed
matcher_t *matcher = NULL;
matcher_janitor_t matcher_janitor(matcher);
bool search_backward = (srch_flags & BIN_SEARCH_BACKWARD) != 0;
if ( search_backward )
matcher = new backward_matcher_t(found_ea, this, ptns, srch_flags, errbuf);
else
matcher = new forward_matcher_t(found_ea, this, ptns, srch_flags, errbuf);
drc_t drc = matcher->walk_memory_ranges(start_ea, end_ea);
if ( drc != DRC_ERROR )
return drc; //-V773 without releasing the 'matcher' pointer
ea_t failed_ea = matcher->get_failed_address();
if ( failed_ea != BADADDR )
{
debdeb("dbg_bin_search failed to read memory at %a\n", failed_ea);
if ( errbuf != NULL && errbuf->empty() )
errbuf->sprnt("Failed to read memory at %a\n", failed_ea);
}
return DRC_ERROR;
}
#else // TEST
//--------------------------------------------------------------------------
drc_t binary_search(
ea_t *found_ea,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring *errbuf)
{
matcher_t *matcher;
if ( (srch_flags & BIN_SEARCH_BACKWARD) != 0 )
matcher = new backward_matcher_t(found_ea, NULL, ptns, srch_flags, errbuf);
else
matcher = new forward_matcher_t(found_ea, NULL, ptns, srch_flags, errbuf);
drc_t drc = matcher->find(start_ea, end_ea);
delete matcher;
return drc;
}
//---------------------------------------------------------------------------
inline bool cmpbytes(
const uchar *ptr,
uchar b,
const uchar *pptr,
size_t ptnsize,
bool sense_case)
{
if ( sense_case )
return *ptr == b && memcmp(ptr+1, pptr, ptnsize) == 0; //lint !e670
if ( qtoupper(b) != qtoupper(*ptr) )
return false;
++ptr;
for ( int i=0; i < ptnsize; ++i, ++ptr, ++pptr )
{
if ( qtoupper(*ptr) != qtoupper(*pptr) )
return false;
}
return true;
}
//---------------------------------------------------------------------------
void *memmem(
const void *buf,
size_t bufsize,
const void *ptn,
size_t ptnsize,
bool sense_case)
{
if ( int(ptnsize) <= 0 || int(bufsize) < 0 || ptnsize > bufsize )
return NULL;
const uchar *ptr = (const uchar *)buf;
const uchar *const end = ptr + bufsize - ptnsize + 1;
const uchar *pptr = (const uchar *)ptn;
uchar b = *pptr++;
ptnsize--;
while ( ptr < end )
{
if ( cmpbytes(ptr, b, pptr, ptnsize, sense_case) )
return (void *)ptr;
++ptr;
}
return NULL;
}
//---------------------------------------------------------------------------
void *memmemr(
const void *buf,
size_t bufsize,
const void *ptn,
size_t ptnsize,
bool sense_case)
{
if ( int(ptnsize) <= 0 || int(bufsize) < 0 || ptnsize > bufsize )
return NULL;
const uchar *ptr = (const uchar *)buf + bufsize - ptnsize;
const uchar *const start = (const uchar *)buf;
const uchar *pptr = (const uchar *)ptn;
uchar b = *pptr++;
ptnsize--;
while ( start <= ptr )
{
if ( cmpbytes(ptr, b, pptr, ptnsize, sense_case) )
return (void *)ptr;
--ptr;
}
return NULL;
}
//--------------------------------------------------------------------------
drc_t simple_binary_search(
eavec_t *found_eas,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring * /*errbuf*/)
{
if ( start_ea >= end_ea || start_ea > sizeof(memory) )
return DRC_FAILED;
bool sense_case = (srch_flags & BIN_SEARCH_CASE) != 0;
found_eas->clear();
bool backward = (srch_flags & BIN_SEARCH_BACKWARD) != 0;
for ( compiled_binpat_vec_t::const_iterator p=ptns.begin();
p != ptns.end();
++p )
{
const bytevec_t &vec = p->bytes;
uchar *start = memory + start_ea;
asize_t nbytes = qmin(sizeof(memory)-start_ea, end_ea-start_ea);
uchar *f = (uchar *)(backward
? memmemr(start, nbytes, vec.begin(), vec.size(), sense_case)
: memmem(start, nbytes, vec.begin(), vec.size(), sense_case));
if ( f == NULL )
continue;
ea_t idx = f - memory;
if ( idx >= end_ea )
continue;
found_eas->push_back(idx);
}
return found_eas->empty() ? DRC_FAILED : DRC_OK;
}
//--------------------------------------------------------------------------
static bool check(qstring *found1_s, const eavec_t &found1, ea_t found2)
{
bool ok = found1.empty() && found2 == BADADDR;
for ( int k=0; k < found1.size(); ++k )
{
if ( found1[k] == found2 )
ok = true;
if ( k > 0 )
found1_s->append(',');
found1_s->cat_sprnt("%a", found1[k]);
}
if ( found1_s->empty() )
found1_s->sprnt("%a", BADADDR);
return ok;
}
//--------------------------------------------------------------------------
int main(int argc, char *argv[])
{
bool sense_case = false;
int max_ptns = 3;
for ( int i=1; i < argc; ++i )
{
char argch = argv[i][0];
if ( argch == 'C' )
{
sense_case = true;
}
else if ( '0' < argch && argch <= '9' )
{
max_ptns = argch - '0';
}
}
msg("Test bin_search with %d pattern[s] and %s\n",
max_ptns,
sense_case ? "case sensitive" : "case ignored");
for ( int i=0; i < sizeof(memory); i++ )
memory[i] = i;//rand();
for ( int i=0; i < 1000000; i++ )
{
// prepare a pattern for searching
compiled_binpat_vec_t ptns;
int ptns_cnt = max_ptns == 1 ? 1 : (rand() % max_ptns) + 1;
ptns.resize(ptns_cnt);
qstring out;
for ( int c=0; c < ptns.size(); c++ )
{
size_t off = rand() % sizeof(memory);
size_t len = (rand() % sizeof(memory)/20) + 1;
if ( (rand() % 50) == 0 )
len += 8;
compiled_binpat_t &pat = ptns[c];
pat.bytes.resize(len, 0xFF);
size_t copyable = qmin(sizeof(memory)-off, len);
memcpy(pat.bytes.begin(), &memory[off], copyable);
if ( c > 0 )
out.append(",");
out.cat_sprnt("%X:%X", int(off), int(len));
// if some rare cases make the pattern possibly insearchable
if ( (rand() % 50) == 0 )
{
pat.bytes[0] = 0xAA;
out.append("-");
}
}
ea_t start_ea = rand() % sizeof(memory);
ea_t end_ea = start_ea + (rand() % sizeof(memory));
if ( end_ea > sizeof(memory) )
end_ea = sizeof(memory); // no need to test out of memory
int flags = sense_case ? BIN_SEARCH_CASE : BIN_SEARCH_NOCASE;
eavec_t found1;
simple_binary_search(&found1, start_ea, end_ea, ptns, flags|BIN_SEARCH_FORWARD, NULL);
ea_t found2 = BADADDR;
binary_search(&found2, start_ea, end_ea, ptns, flags|BIN_SEARCH_FORWARD, NULL);
qstring found1_s;
bool ok = check(&found1_s, found1, found2);
msg("%3d find (%s) in (%a..%a) => %s %a\n", i, out.c_str(), start_ea, end_ea, found1_s.c_str(), found2);
if ( !ok )
{
msg("FAILED!\n");
return 1;
}
found1.clear();
simple_binary_search(&found1, start_ea, end_ea, ptns, flags|BIN_SEARCH_BACKWARD, NULL);
found2 = BADADDR;
binary_search(&found2, start_ea, end_ea, ptns, flags|BIN_SEARCH_BACKWARD, NULL);
found1_s.qclear();
ok = check(&found1_s, found1, found2);
msg("%3d findr(%s) in (%a..%a) => %s %a\n", i, out.c_str(), start_ea, end_ea, found1_s.c_str(), found2);
if ( !ok )
{
msg("FAILED!\n");
return 1;
}
}
msg("OK\n");
return 0;
}
#endif // TEST

View File

@@ -0,0 +1,34 @@
/*
* Interactive disassembler (IDA).
* ALL RIGHTS RESERVED.
* Copyright (c) 1990-2021 Hex-Rays
*
*
* This file defines the Bochs Debugger module extension functions.
* Use debugger_t->get_debmod_extensions() to retrieve this structure.
*
*/
#ifndef __BOCHSEXT__
#define __BOCHSEXT__
#pragma pack(push, 1)
#define BOCHSEXT_VER 1
struct bochsext_t
{
// the structure version
uint32 version;
// Sends an arbitrary command to Bochs internal debugger
// cmd - command to send
// out - pointer to qstring that will hold the output of the command
// Returns: true if ok; false if failed to send command to bochs or receive
// a reply
bool (idaapi *send_command)(const char *cmd, qstring *out);
};
#pragma pack(pop)
#endif

View File

@@ -0,0 +1,62 @@
/*
* Interactive disassembler (IDA).
* ALL RIGHTS RESERVED.
* Copyright (c) 1990-2021 Hex-Rays
*
*
* This file defines the functions prototypes that are exported by bochsys.dll
*
*
*/
#ifndef __BOCHSYS_DLL__
#define __BOCHSYS_DLL__
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define BX_CALLCONV WINAPI
typedef wchar_t wchar16_t;
//CASSERT(sizeof(wchar16_t) == 2);
//--------------------------------------------------------------------------
// These functions are similar to MS Windows functions. Please refer
// to the SDK documentation for more information on how to use them.
extern FARPROC WINAPI BxGetProcAddress(HMODULE hMod, LPCSTR ProcName);
extern HMODULE WINAPI BxGetModuleHandleA(LPCSTR ModuleFileName);
extern DWORD WINAPI BxGetModuleFileNameA(HINSTANCE hModule, LPCSTR lpFilename, DWORD nSize);
extern DWORD WINAPI BxGetModuleFileNameW(HINSTANCE hModule, LPWSTR lpFilename, DWORD nSize);
extern HMODULE WINAPI BxLoadLibraryA(LPCTSTR lpFileName);
extern LPVOID WINAPI BxVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
extern BOOL WINAPI BxVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
extern DWORD WINAPI BxExitProcess(DWORD);
extern DWORD WINAPI BxGetTickCount(VOID);
extern BOOL WINAPI BxVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
extern DWORD WINAPI BxWin32SetLastError(DWORD ErrorCode);
extern DWORD WINAPI BxWin32GetLastError(VOID);
extern LPCSTR WINAPI BxWin32GetCommandLineA(VOID);
extern LPWSTR WINAPI BxWin32GetCommandLineW(VOID);
extern LPCSTR WINAPI BxWin32GetEnvironmentStringsA(VOID);
extern LPWSTR WINAPI BxWin32GetEnvironmentStringsW(VOID);
extern LPVOID WINAPI BxWin32TlsGetValue(DWORD dwTlsIndex);
extern BOOL WINAPI BxWin32TlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue);
extern BOOL WINAPI BxWin32TlsFree(DWORD dwTlsIndex);
extern DWORD WINAPI BxWin32TlsAlloc(VOID);
extern DWORD WINAPI BxWin32FlsAlloc(VOID);
extern char * WINAPI BxStrCpyA(char *Dst, char *Src);
extern wchar16_t * WINAPI BxStrCpyW(wchar16_t *Dst, wchar16_t *Src);
extern char * WINAPI BxStrCatA(char *Dst, char *Src);
extern wchar16_t * WINAPI BxStrCatW(wchar16_t *Dst, wchar16_t *Src);
//--------------------------------------------------------------------------
// Installs an exception handler. Only one exception handler
// can be installed at one time. You need to uninstall one
// before reinstalling another.
// These two functions will return non-zero on success.
typedef DWORD (*PEXCEPTION_HANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD *, PCONTEXT,struct _EXCEPTION_REGISTRATION_RECORD **);
extern DWORD WINAPI BxInstallSEH(PEXCEPTION_HANDLER Handler);
extern DWORD WINAPI BxUninstallSEH();
#endif

View File

@@ -0,0 +1,271 @@
// This file contains bochs startup and exit procedures bochs_startup() and bochs_exit()
// The former is called when the process starts
// The latter is called when the process is about to exit
// Both functions cause IDA debugger to suspend if they return a non zero value
// This section declares which DLLs will be available:
// * Use the "stub" to mark a dll for stubbing
// * Use the "load" to mark a dll to be loaded as is
// The "load" keyword has an additional attributes called "R0UserEntry"
// This attribute is used to designate an exported function that will be called from ring0
// Such a mechanism is useful to extend bochsys kernel or even replace it after it is loaded
// One simple application is to modify the IDT and add R3 callable interrupts into your kernel
// Only lines containing three forward slashes ("/") are processed:
/// stub ntdll.dll
/// stub kernel32.dll
/// stub user32.dll
/// stub shell32.dll
/// stub shlwapi.dll
/// stub urlmon.dll
/// stub advapi32.dll
/// stub mswsock.dll
/// stub wininet.dll
/// stub msvcrt.dll
/// stub gdi32.dll
/// stub ole32.dll
/// stub wsock32.dll
/// stub ws2_32.dll
// Define our own environment variables.
// (add triple slashes to enable)
// env path=c:\games\bin;d:\bin\asdf\
// env userprofile=c:\games\
// Define your dependency mappings
// (add triple slashes to enable the following lines)
// map /home/guest/sys_dlls/user32.dll=d:\winnt\system32\user32.dll
// map /home/guest/sys_dlls/shell32.dll=d:\winnt\system32\shell32.dll
// map /home/guest/sys_dlls/kernel32.dll=d:\winnt\system32\kernel32.dll
// map /home/guest/sys_dlls/shlwapi.dll=d:\winnt\system32\shlwapi.dll
// map /home/guest/sys_dlls/urlmon.dll=d:\winnt\system32\urlmon.dll
// map /home/guest/sys_dlls/mswsock.dll=d:\winnt\system32\mswsock.dll
// map /home/guest/sys_dlls/wininet.dll=d:\winnt\system32\wininet.dll
// map /home/guest/sys_dlls/msvcrt.dll=d:\winnt\system32\msvcrt.dll
// map /home/guest/sys_dlls/gdi32.dll=d:\winnt\system32\gdi32.dll
// map /home/guest/sys_dlls/ntdll.dll=d:\winnt\system32\ntdll.dll
// map /home/guest/sys_dlls/advapi32.dll=d:\winnt\system32\advapi32.dll
// Define additional DLL path
// (add triple slashes to enable the following lines)
// path /home/guest/sys_dlls/=c:\winnt\system32\
// Bochs debugger plugin also allows you to specify the DLL path through the environment variable IDABXPATHMAP
// (It is possible to specify more than one key/value pair by separating them with a semi-colon)
// For example:
// $ export IDABXPATHMAP="/home/guest/sys_dlls/=c:/winnt/system32/;/home/user2/other_dlls/=c:/program files/common files/3rd party/"
// Similarly, one can specify the environment variables through the environment variable IDABXENVMAP
// (it is possible to specify more than one key/value pair by separating them with a "++")
// For example:
// $ export IDABXENVMAP="TMP=c:/Users/Guest/Temp++SystemDrive=C:++windir=c:/windows/"
//
// Please note that the forward slashes "/" in the value part of the key/value pair will always be replaced with a backslash
//
// The following are oneshot options. Once set they cannot be unset.
// To define them simply preceed the option name with triple slashes.
// - nosearchpath: Disables SearchPath() use for finding DLLs (this option is applicable on MS Windows only).
// By turning this option, Bochs plugin will try to load DLLs from the current directory.
// It useful for loading certain (old or new) versions of system DLLs instead of the ones currently installed
// on the system.
// - noactivationcontext: Disables the use of "Activation Context" (this option is applicable on MS Windows only).
//
//
// For loading drivers, you may uncomment the following stub definition(s):
//
// stub ntoskrnl.exe
// For example: to load a dll as is: load mydll.dll
// For example: to load a dll as is and specify a user R0 entry: load mydll.dll R0UserEntry=MyExportedFunc
#include <idc.idc>
//--------------------------------------------------------------------------
// IDC scripts that will be available during the debugging session
// MS Windows related functions
// ------------------------------
// BochsVirtXXXX functions allocate/free virtual memory in the emulated session.
// The "size" parameter is always rounded to a page.
//
//
// Allocate virtual memory
// This function emulates the VirtualAlloc function from MS Windows
// addr - the preferred address for the allocation. Zero means no preference
// size - size of the block to allocate
// writable - should be allocated memory be wrtiable?
// Currently only read/write page protections are supported
// Returns: the address of the allocated block or zero
//
// long BochsVirtAlloc(long addr, long size, bool writable);
//
//
// Change protection of memory page
// This function emulates the VirtualProtect function from MS Windows
// addr - the desired address to change protection.
// size - size of the block
// attr - the new page attribute:
// 0 = Read only
// 1 = Read/Write
// Returns: the old protection value or -1 on failure
//
// long BochsVirtProtect(long addr, long size, long attr);
//
//
// Free virtual memory
// This function emulates the VirtualFree function from MS Windows
// addr - the address of previously allocated memory block
// size - the size of the block. If zero, then the entire block at addr
// will be freed.
// Returns: success
//
// bool BochsVirtFree(long addr, long size);
//
//
// Returns the base address of a given module name
// module_name - The name of the module.
// The name can be full path or filename with extension, or simply filename without extension
// Returns: zero if it fails
//
// long BochsGetModuleHandle(string module_name);
//
//
// Returns a procedure's address
// This function calls the internal GetProcAddress to resolve function addresses.
// hmod - the module handle
// procname - name of the procedure inside the module
// Returns: the zero if procedure not found, otherwise the address
//
// long BochsGetProcAddress(long hmod, string procname);
//
//
// Returns the module name given its base address
// module_handle: the base address of a given module
// Returns: empty string if module was not found
//
// string BochsGetModuleFileName(long module_handle)
//
//
// Returns the command line value passed to the application
//
// string BochsGetCommandLine()
//
//
// Set last error code
// This function emulates the SetLastError function from MS Windows.
// It writes the specified code to TIB.
// error_code - new error code to set
// Returns: success
//
// success BochsWin32SetLastError(long error_num);
//
//
// Other functions:
// -------------------
//
//
// Sends arbitrary commands to the internal debugger of BOCHS. The output is returned as a string.
// This is useful for example to send some commands to BOCHS that are not exposed via the GUI of IDA.
// command: the command you want to send
// Returns: output string or empty string if it failed
//
// string send_dbg_command(string command)
//
//
// Retrieves the parameter value passed to an IDC script that is implementing a given API.
// This same function can be implemented with this expression: #define BX_GETPARAM(n) get_wide_dword(esp+4*(n+1))
// arg_num: the argument number (starting by one)
// Returns: the value or zero in case it fails
//
// string BochsGetParam(long arg_num)
//
//
// Calls a function inside Bochs
// This function can call functions inside Bochs. Very useful if you want to call
// functions in the user's code. The arguments are pushed from right to left.
// func_ptr - The address of the function to be called
// argN - a set of dwords that contain the arguments.
// Arguments can be numbers or pointers
// Returns: success
//
// long BochsCall(long func_ptr, arg1, arg2, ...);
//
//
// These functions will return the total physical memory amount and the remaining free
// memory in bytes.
// Returns: memory size in bytes
//
// long BochsGetFreeMem()
// long BochsGetMaxMem()
//
// ----------------------------------------------------------------------------
static BochsPatchDbgDword(ea, dv)
{
auto i;
for (i=0;i<4;i++)
{
patch_dbg_byte(ea, dv & 0xFF);
ea = ea + 1;
dv = dv >> 8;
}
}
// ----------------------------------------------------------------------------
// Utility function that can be used as a conditional breakpoint condition
// in order to skip to the next instruction w/o suspending IDA
static bochs_skipnext()
{
Eip = next_head(eip, BADADDR);
return 0;
}
// ----------------------------------------------------------------------------
// Utility function that can be used as a conditional breakpoint condition
// in order to execute the contents of the comments at the bp location
static bochs_execidc_comments()
{
exec_idc(Comment(eip));
return 0;
}
// ----------------------------------------------------------------------------
// Utility function used to dump registers. The output can be used as a comment
// with the bochs_execidc_comments() bp condition
static bochs_dump_registers()
{
msg("eax=0x%x;ebx=0x%x;ecx=0x%x;edx=0x%x;esi=0x%x;edi=0x%x;ebp=0x%x;", eax, ebx, ecx, edx, esi, edi, ebp);
}
// ----------------------------------------------------------------------------
static bochs_startup()
{
msg("Bochs debugger has been initialized.\n");
return 0;
}
// ----------------------------------------------------------------------------
static bochs_exit()
{
msg("Bochs debugger has been terminated.\n");
return 0;
}

View File

@@ -0,0 +1,2 @@
///func=MessageBoxA entry=bxtest.MyMessageBox

View File

@@ -0,0 +1,89 @@
#include "bochsys.h"
#include <windows.h>
//--------------------------------------------------------------------------
// dummy entry point so that linker does not use entrypoints from CRT
DWORD WINAPI Entry(DWORD a, DWORD b, DWORD c)
{
return 0;
}
//--------------------------------------------------------------------------
// This function will be called by bochsys.dll from R0 before switching to R3
// This is even called before TLS callbacks
void WINAPI MyR0Entry(VOID)
{
__asm
{
nop
mov dx, 0378h
in eax, dx
nop
nop
}
}
//--------------------------------------------------------------------------
DWORD MyHandler(
PEXCEPTION_RECORD rec,
struct _EXCEPTION_REGISTRATION_RECORD *reg,
PCONTEXT ctx,
struct _EXCEPTION_REGISTRATION_RECORD **reg2)
{
ctx->Eip += 2;
return ExceptionContinueExecution;
}
//--------------------------------------------------------------------------
void BuggyFunction()
{
BxInstallSEH(MyHandler);
__asm
{
xor eax, eax
mov eax, [eax]
}
BxUninstallSEH();
}
//--------------------------------------------------------------------------
// In this function, BxXXXXXX functions are used from the bochsys library
int __stdcall MyMessageBox(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType)
{
char *p;
int i;
// Allocate memory
p = BxVirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_READWRITE);
// Fill the memory
for (i=1;i<=0x1000;i++)
*p++ = i & 0xFF;
// Resolve an entry and call it
(VOID (__stdcall *)(int, int)) BxGetProcAddress(BxLoadLibraryA("kernel32.dll"), "Beep")(5, 1);
// Call a function that might cause an exception
BuggyFunction();
return 0;
}
//--------------------------------------------------------------------------
// In this function we import from user32 and kernel32
// (because VirtualAlloc->BxVirtualAlloc and MessageBoxA->bxtest.MyMessageBox are redirected and implemented)
int __stdcall MyRoutine(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType)
{
VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_READWRITE);
MessageBoxA(0, "hey", "info", MB_OK);
return 0;
}

View File

@@ -0,0 +1,4 @@
EXPORTS
MyMessageBox
MyRoutine
MyR0Entry

Binary file not shown.

View File

@@ -0,0 +1,11 @@
@echo off
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\cl.exe" -c /Zl /Gd /Tc bxtest.c "/IC:/Program Files (x86)/Windows Kits/8.1/Include/um" "/IC:/Program Files (x86)/Windows Kits/8.1/Include/shared" "/IC:/PROGRA~2/WI3CF2~1/10/Include/10.0.10150.0/ucrt" /I"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"
if errorlevel 1 goto end
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\link.exe" bxtest.obj bochsys.lib kernel32.lib user32.lib /OUT:bxtest.dll /ENTRY:Entry /def:bxtest.def /DRIVER /SAFESEH:NO /NODEFAULTLIB /SUBSYSTEM:WINDOWS /LIBPATH:"C:\Program Files\Microsoft Visual Studio 14.0\VC\Lib" /LIBPATH:"C:/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x86"
if errorlevel 1 goto end
if exist bxtest.obj del bxtest.obj
if exist bxtest.exp del bxtest.exp
if exist bxtest.lib del bxtest.lib
:end

View File

@@ -0,0 +1,90 @@
Custom DLLs for emulated MS Windows environment
-----------------------------------------------
This directory contains files that demonstrate how to build a custom DLL
for the PE loader of the Bochs debugger.
compile.bat shows how to build a custom DLL with the MS compiler/linker.
The general rule is not to link with runtime libraries, but linking with
import libraries is ok.
"bxtest.c" demonstrates how to call functions in bochsys.dll.
"bochsys.h" has the list of functions that can be called from custom DLLs.
"bochsys.lib" is the corresponding import library
Custom DLLs must be mentioned in plugins\bochs\startup.idc.
For that please add a line like this:
/// load bxtest.dll
This will cause the DLL to be present in the memory space of the debugged process.
For the custom DLL to be useful, its exported functions should be connected
to API function names. For example, the following line redirects MessageBoxA
to bxtest.MyMessageBox:
/// func=MessageBoxA entry=bxtest.MyMessageBox
The exact format of the startup.idc file is explained in its header.
On the other hand, it is also possible to write a custom DLL that replaces system
DLLs like kernel32.dll or user32.dll.
The "load" command has an additional parameter "R0UserEntry=MyR0Entry" used as:
///load bxtest.dll R0UserEntry=MyR0Entry
Which means that bxtest.dll should be loaded into the process memory and
that this DLL has an exported entry that should be called by bochsys from ring0.
Such a facility is ideal if you're looking to replace or enhance bochsys's kernel.
To test how MessageBoxA is redirected to MyMessageBox, please follow these
steps:
- compile and link bxtest.dll with compile.bat
(we provide ready-to-use bxtest.dll for your convenience, so you
skip this step)
- add two lines mentioned above to startup.idc and api_user32.idc respectively
- load test.pe into IDA and select Bochs debugger
- run it and single step into the MessageBoxA function
With any questions, please contact us at support@hex-rays.com
Bochs plugin debugger extensions
-----------------------------------
Bochs extensions allow for accessing extended debugger functionality.
To get and use the extensions, query the currently loaded debugger using
get_debmod_extensions(). Usually it returns a pointer to a structure with
pointers to functions. Please follow this example:
#include "bochsext.h"
void idaapi run(int)
{
if ( dbg == NULL )
{
msg("dbg == NULL\n");
return;
}
const bochsext_t *ext = (const bochsext_t *)dbg->get_debmod_extensions();
if ( ext == NULL )
{
msg("no debugger extensions!\n");
return;
}
// dump 10 bytes from physical memory at 0x0
qstring out;
if ( !ext->send_command("xp /10mb 0x0\r\n", &out) )
{
msg("failed to send command!\n");
return;
}
msg("->result=%s\n", out.c_str());
}

View File

@@ -0,0 +1,36 @@
; #########################################################################
.386
.model flat, stdcall
option casemap :none ; case sensitive
; #########################################################################
include d:\masm32\include\windows.inc
include d:\masm32\include\user32.inc
include d:\masm32\include\kernel32.inc
includelib d:\masm32\lib\user32.lib
includelib d:\masm32\lib\kernel32.lib
; #########################################################################
; --------------------------------------------------------
.data
szDlgTitle db "Minimum MASM",0
szMsg db " --- Assembler Pure and Simple --- ",0
.code
start:
; --------------------------------------------------------
; script
push MB_OK
push offset szDlgTitle
push offset szMsg
push 0
call MessageBox
; --------------------------------------------------------
; idacall
push -2
call ExitProcess
end start

Binary file not shown.

View File

@@ -0,0 +1,857 @@
//
// This file is included from other files, do not directly compile it.
// It contains the debugger_t structure definition and a few other helper functions
//
#include <loader.hpp>
#include <segregs.hpp>
#include <network.hpp>
bool plugin_inited;
bool debugger_inited;
#define IS_GDB_DEBUGGER (DEBUGGER_ID == DEBUGGER_ID_GDB_USER || DEBUGGER_ID == DEBUGGER_ID_ARM_IPHONE_USER || DEBUGGER_ID == DEBUGGER_ID_XNU_USER)
#if TARGET_PROCESSOR == PLFM_386
#define REGISTERS x86_registers
#define REGISTERS_SIZE qnumber(x86_registers)
#define REGISTER_CLASSES x86_register_classes
#define REGISTER_CLASSES_DEFAULT X86_RC_GENERAL
#define READ_REGISTERS x86_read_registers
#define WRITE_REGISTER x86_write_register
#if !IS_GDB_DEBUGGER
#define is_valid_bpt is_x86_valid_bpt
#endif
#define BPT_CODE X86_BPT_CODE
#define BPT_CODE_SIZE X86_BPT_SIZE
#elif TARGET_PROCESSOR == PLFM_ARM
#define REGISTERS arm_registers
#define REGISTERS_SIZE qnumber(arm_registers)
#define REGISTER_CLASSES arm_register_classes
#define REGISTER_CLASSES_DEFAULT ARM_RC_GENERAL
#define READ_REGISTERS s_read_registers
#define WRITE_REGISTER s_write_register
#if !IS_GDB_DEBUGGER
#define is_valid_bpt is_arm_valid_bpt
#else
#define is_valid_bpt gdb_valid_bpt
#endif
#define BPT_CODE ARM_BPT_CODE
#define BPT_CODE_SIZE ARM_BPT_SIZE
#elif TARGET_PROCESSOR == PLFM_DALVIK
#define BPT_CODE { 0 }
#define BPT_CODE_SIZE 0
#define READ_REGISTERS s_read_registers
#define WRITE_REGISTER s_write_register
#define is_valid_bpt is_dalvik_valid_bpt
#elif IS_GDB_DEBUGGER
#define REGISTERS NULL
#define REGISTERS_SIZE 0
#define REGISTER_CLASSES NULL
#define REGISTER_CLASSES_DEFAULT 0
#define READ_REGISTERS simple_read_registers
#define WRITE_REGISTER simple_write_register
#define is_valid_bpt gdb_valid_bpt
#define BPT_CODE { 0 }
#define BPT_CODE_SIZE 0
#else
#error This processor is not supported yet
#endif
static const uchar bpt_code[] = BPT_CODE;
//--------------------------------------------------------------------------
int idaapi is_ok_bpt(bpttype_t type, ea_t ea, int len)
{
int ret = is_valid_bpt(type, ea, len);
if ( ret != BPT_OK )
return ret;
else
return g_dbgmod.dbg_is_ok_bpt(type, ea, len);
}
//--------------------------------------------------------------------------
// For ARM, we have to set the low bit of the address to 1 for thumb mode
#if DEBUGGER_ID == DEBUGGER_ID_ARM_LINUX_USER
static drc_t idaapi arm_update_bpts(
int *nbpts,
update_bpt_info_t *bpts,
int nadd,
int ndel,
qstring *errbuf)
{
// This function is called from debthread, but to use get_sreg() we must
// switch to the mainthread
struct ida_local arm_bptea_fixer_t : public exec_request_t
{
update_bpt_info_t *bpts;
update_bpt_info_t *e;
qvector<ea_t *> thumb_mode;
virtual int idaapi execute(void) override
{
for ( update_bpt_info_t *b=bpts; b != e; b++ )
{
if ( b->type == BPT_SOFT && get_sreg(b->ea, ARM_T) == 1 )
{
b->ea++; // odd address means that thumb bpt must be set
thumb_mode.push_back(&b->ea);
}
}
return 0;
}
arm_bptea_fixer_t(update_bpt_info_t *p1, update_bpt_info_t *p2)
: bpts(p1), e(p2) {}
};
arm_bptea_fixer_t abf(bpts, bpts+nadd);
execute_sync(abf, MFF_READ);
drc_t drc = s_update_bpts(nbpts, bpts, nadd, ndel, errbuf);
// reset the odd bit because the addresses are required by the caller
for ( int i=0; i < abf.thumb_mode.size(); i++ )
(*abf.thumb_mode[i])--;
return drc;
}
#define s_update_bpts arm_update_bpts
#endif
//--------------------------------------------------------------------------
static drc_t idaapi update_bpts(
int *nbpts,
update_bpt_info_t *bpts,
int nadd,
int ndel,
qstring *errbuf)
{
bool valid_bpt_exists = false;
update_bpt_info_t *e = bpts + nadd;
for ( update_bpt_info_t *b=bpts; b != e; b++ )
{
if ( b->code == BPT_SKIP )
continue;
b->code = is_valid_bpt(b->type, b->ea, b->size);
if ( b->code == BPT_OK )
valid_bpt_exists = true;
}
if ( !valid_bpt_exists && ndel == 0 )
{
if ( nbpts != NULL )
*nbpts = 0;
return DRC_OK; // none of bpts is writable
}
drc_t drc = s_update_bpts(nbpts, bpts, nadd, ndel, errbuf);
return drc;
}
//--------------------------------------------------------------------------
#ifndef REMOTE_DEBUGGER
// another copy of this function (for remote debugging) is defined in dbg_rpc_handler.cpp
int send_ioctl(
void *,
int fn,
const void *buf,
size_t size,
void **poutbuf,
ssize_t *poutsize)
{
return g_dbgmod.handle_ioctl(fn, buf, size, poutbuf, poutsize);
}
#endif
//--------------------------------------------------------------------------
THREAD_SAFE int debmod_t::send_debug_names_to_ida(
ea_t *addrs,
const char *const *names,
int qty)
{
return ::send_debug_names_to_ida(addrs, names, qty);
}
//---------------------------------------------------------------------------
THREAD_SAFE int send_debug_names_to_ida(
ea_t *addrs,
const char *const *names,
int qty)
{
struct debug_name_handler_t : public exec_request_t
{
ea_t *addrs;
const char *const *names;
int qty;
debug_name_handler_t(ea_t *_addrs, const char *const *_names, int _qty)
: addrs(_addrs), names(_names), qty(_qty) {}
int idaapi execute(void) override
{
set_arm_thumb_modes(addrs, qty);
return set_debug_names(addrs, names, qty);
}
};
debug_name_handler_t dnh(addrs, names, qty);
return execute_sync(dnh, MFF_WRITE);
}
//--------------------------------------------------------------------------
THREAD_SAFE int debmod_t::send_debug_event_to_ida(
const debug_event_t *ev,
int rqflags)
{
return ::send_debug_event_to_ida(ev, rqflags);
}
//---------------------------------------------------------------------------
THREAD_SAFE int send_debug_event_to_ida(
const debug_event_t *ev,
int rqflags)
{
return handle_debug_event(ev, rqflags);
}
//--------------------------------------------------------------------------
THREAD_SAFE int import_dll(const import_request_t &req)
{
struct dll_importer_t : public exec_request_t
{
const import_request_t &req;
dll_importer_t(const import_request_t &_req) : req(_req) {}
int idaapi execute(void) override
{
return g_dbgmod.import_dll(req) ? 0 : 1;
}
};
dll_importer_t di(req);
return execute_sync(di, MFF_WRITE);
}
//--------------------------------------------------------------------------
#if TARGET_PROCESSOR != PLFM_ARM
void set_arm_thumb_modes(ea_t * /*addrs*/, int /*qty*/)
{
}
#endif
//--------------------------------------------------------------------------
// installs or uninstalls debugger specific idc functions
bool add_idc_funcs(const ext_idcfunc_t efuncs[], size_t nfuncs, bool reg)
{
if ( reg )
{
for ( int i=0; i < nfuncs; i++ )
if ( !add_idc_func(efuncs[i]) )
return false;
}
else
{
for ( int i=0; i < nfuncs; i++ )
if ( !del_idc_func(efuncs[i].name) )
return false;
}
return true;
}
//--------------------------------------------------------------------------
static drc_t init_debugger(
const char *hostname,
int port_num,
const char *password,
qstring *errbuf)
{
g_dbgmod.dbg_set_debugging((debug & IDA_DEBUG_DEBUGGER) != 0);
if ( !s_open_remote(hostname, port_num, password, errbuf) )
return DRC_FAILED;
uint32_t flags2 = 0;
drc_t drc = s_init(&flags2, errbuf);
if ( drc != DRC_OK )
{
s_close_remote();
return drc;
}
debugger.flags2 |= flags2;
#if defined(REMOTE_DEBUGGER) && !defined(NO_OPEN_FILE)
setflag(debugger.flags2, DBG_HAS_OPEN_FILE, true);
#endif
#ifdef HAVE_UPDATE_CALL_STACK
setflag(debugger.flags2, DBG_HAS_UPDATE_CALL_STACK, true);
#endif
#ifdef HAVE_APPCALL
setflag(debugger.flags2, DBG_HAS_APPCALL, true);
#endif
#ifdef HAVE_MAP_ADDRESS
setflag(debugger.flags2, DBG_HAS_MAP_ADDRESS, true);
#endif
debugger_inited = true;
processor_specific_init();
register_idc_funcs(true);
init_dbg_idcfuncs(true);
#if DEBUGGER_ID == DEBUGGER_ID_X86_IA32_WIN32_USER || DEBUGGER_ID == DEBUGGER_ID_X86_IA32_BOCHS
install_x86seh_menu();
#endif
return DRC_OK;
}
//--------------------------------------------------------------------------
static drc_t term_debugger(void)
{
if ( debugger_inited )
{
debugger_inited = false;
#if DEBUGGER_ID == DEBUGGER_ID_X86_IA32_WIN32_USER || DEBUGGER_ID == DEBUGGER_ID_X86_IA32_BOCHS
remove_x86seh_menu();
#endif
init_dbg_idcfuncs(false);
register_idc_funcs(false);
processor_specific_term();
g_dbgmod.dbg_term();
return s_close_remote();
}
return DRC_FAILED;
}
//--------------------------------------------------------------------------
static ssize_t idaapi idd_notify(void *, int msgid, va_list va)
{
int retcode = DRC_NONE;
qstring *errbuf;
switch ( msgid )
{
case debugger_t::ev_init_debugger:
{
const char *hostname = va_arg(va, const char *);
int portnum = va_arg(va, int);
const char *password = va_arg(va, const char *);
errbuf = va_arg(va, qstring *);
QASSERT(1522, errbuf != NULL);
retcode = init_debugger(hostname, portnum, password, errbuf);
}
break;
case debugger_t::ev_term_debugger:
retcode = term_debugger();
break;
case debugger_t::ev_get_processes:
{
procinfo_vec_t *procs = va_arg(va, procinfo_vec_t *);
errbuf = va_arg(va, qstring *);
retcode = g_dbgmod.dbg_get_processes(procs, errbuf);
}
break;
case debugger_t::ev_start_process:
{
const char *path = va_arg(va, const char *);
const char *args = va_arg(va, const char *);
const char *startdir = va_arg(va, const char *);
uint32 dbg_proc_flags = va_arg(va, uint32);
const char *input_path = va_arg(va, const char *);
uint32 input_file_crc32 = va_arg(va, uint32);
errbuf = va_arg(va, qstring *);
retcode = s_start_process(path,
args,
startdir,
dbg_proc_flags,
input_path,
input_file_crc32,
errbuf);
}
break;
case debugger_t::ev_attach_process:
{
pid_t pid = va_argi(va, pid_t);
int event_id = va_arg(va, int);
uint32 dbg_proc_flags = va_arg(va, uint32);
errbuf = va_arg(va, qstring *);
retcode = s_attach_process(pid, event_id, dbg_proc_flags, errbuf);
}
break;
case debugger_t::ev_detach_process:
retcode = g_dbgmod.dbg_detach_process();
break;
case debugger_t::ev_get_debapp_attrs:
{
debapp_attrs_t *out_pattrs = va_arg(va, debapp_attrs_t *);
g_dbgmod.dbg_get_debapp_attrs(out_pattrs);
retcode = DRC_OK;
}
break;
case debugger_t::ev_rebase_if_required_to:
{
ea_t new_base = va_arg(va, ea_t);
rebase_if_required_to(new_base);
retcode = DRC_OK;
}
break;
case debugger_t::ev_request_pause:
errbuf = va_arg(va, qstring *);
retcode = g_dbgmod.dbg_prepare_to_pause_process(errbuf);
break;
case debugger_t::ev_exit_process:
errbuf = va_arg(va, qstring *);
retcode = g_dbgmod.dbg_exit_process(errbuf);
break;
case debugger_t::ev_get_debug_event:
{
gdecode_t *code = va_arg(va, gdecode_t *);
debug_event_t *event = va_arg(va, debug_event_t *);
int timeout_ms = va_arg(va, int);
*code = g_dbgmod.dbg_get_debug_event(event, timeout_ms);
retcode = DRC_OK;
}
break;
case debugger_t::ev_resume:
{
debug_event_t *event = va_arg(va, debug_event_t *);
retcode = g_dbgmod.dbg_continue_after_event(event);
}
break;
case debugger_t::ev_set_exception_info:
{
exception_info_t *info = va_arg(va, exception_info_t *);
int qty = va_arg(va, int);
g_dbgmod.dbg_set_exception_info(info, qty);
retcode = DRC_OK;
}
break;
case debugger_t::ev_suspended:
{
bool dlls_added = va_argi(va, bool);
thread_name_vec_t *thr_names = va_arg(va, thread_name_vec_t *);
s_stopped_at_debug_event(thr_names, dlls_added);
retcode = DRC_OK;
}
break;
case debugger_t::ev_thread_suspend:
{
thid_t tid = va_argi(va, thid_t);
retcode = g_dbgmod.dbg_thread_suspend(tid);
}
break;
case debugger_t::ev_thread_continue:
{
thid_t tid = va_argi(va, thid_t);
retcode = g_dbgmod.dbg_thread_continue(tid);
}
break;
case debugger_t::ev_set_resume_mode:
{
thid_t tid = va_argi(va, thid_t);
resume_mode_t resmod = va_argi(va, resume_mode_t);
retcode = g_dbgmod.dbg_set_resume_mode(tid, resmod);
}
break;
case debugger_t::ev_read_registers:
{
thid_t tid = va_argi(va, thid_t);
int clsmask = va_arg(va, int);
regval_t *values = va_arg(va, regval_t *);
errbuf = va_arg(va, qstring *);
retcode = READ_REGISTERS(tid, clsmask, values, errbuf);
}
break;
case debugger_t::ev_write_register:
{
thid_t tid = va_argi(va, thid_t);
int regidx = va_arg(va, int);
const regval_t *value = va_arg(va, const regval_t *);
errbuf = va_arg(va, qstring *);
retcode = WRITE_REGISTER(tid, regidx, value, errbuf);
}
break;
case debugger_t::ev_thread_get_sreg_base:
{
ea_t *answer = va_arg(va, ea_t *);
thid_t tid = va_argi(va, thid_t);
int sreg_value = va_arg(va, int);
errbuf = va_arg(va, qstring *);
retcode = g_dbgmod.dbg_thread_get_sreg_base(answer, tid, sreg_value, errbuf);
}
break;
case debugger_t::ev_get_memory_info:
{
meminfo_vec_t *ranges = va_arg(va, meminfo_vec_t *);
errbuf = va_arg(va, qstring *);
retcode = g_dbgmod.dbg_get_memory_info(*ranges, errbuf);
}
break;
case debugger_t::ev_read_memory:
{
size_t *nbytes = va_arg(va, size_t *);
ea_t ea = va_arg(va, ea_t);
void *buffer = va_arg(va, void *);
size_t size = va_arg(va, size_t);
errbuf = va_arg(va, qstring *);
ssize_t code = g_dbgmod.dbg_read_memory(ea, buffer, size, errbuf);
*nbytes = code >= 0 ? code : 0;
retcode = code >= 0 ? DRC_OK : DRC_NOPROC;
}
break;
case debugger_t::ev_write_memory:
{
size_t *nbytes = va_arg(va, size_t *);
ea_t ea = va_arg(va, ea_t);
const void *buffer = va_arg(va, void *);
size_t size = va_arg(va, size_t);
errbuf = va_arg(va, qstring *);
ssize_t code = g_dbgmod.dbg_write_memory(ea, buffer, size, errbuf);
*nbytes = code >= 0 ? code : 0;
retcode = code >= 0 ? DRC_OK : DRC_NOPROC;
}
break;
case debugger_t::ev_check_bpt:
{
int *bptvc = va_arg(va, int *);
bpttype_t type = va_argi(va, bpttype_t);
ea_t ea = va_arg(va, ea_t);
int len = va_arg(va, int);
*bptvc = is_ok_bpt(type, ea, len);
retcode = DRC_OK;
}
break;
case debugger_t::ev_update_bpts:
{
int *nbpts = va_arg(va, int *);
update_bpt_info_t *bpts = va_arg(va, update_bpt_info_t *);
int nadd = va_arg(va, int);
int ndel = va_arg(va, int);
errbuf = va_arg(va, qstring *);
retcode = update_bpts(nbpts, bpts, nadd, ndel, errbuf);
}
break;
case debugger_t::ev_update_lowcnds:
{
int *nupdated = va_arg(va, int *);
const lowcnd_t *lowcnds = va_arg(va, const lowcnd_t *);
int nlowcnds = va_arg(va, int);
errbuf = va_arg(va, qstring *);
retcode = g_dbgmod.dbg_update_lowcnds(nupdated, lowcnds, nlowcnds, errbuf);
}
break;
case debugger_t::ev_open_file:
{
const char *file = va_arg(va, const char *);
uint64 *fsize = va_arg(va, uint64 *);
bool readonly = va_argi(va, bool);
retcode = g_dbgmod.dbg_open_file(file, fsize, readonly);
}
break;
case debugger_t::ev_close_file:
{
int fn = va_arg(va, int);
g_dbgmod.dbg_close_file(fn);
retcode = DRC_OK;
}
break;
case debugger_t::ev_read_file:
{
int fn = va_arg(va, int);
qoff64_t off = va_arg(va, qoff64_t);
void *buf = va_arg(va, void *);
size_t size = va_arg(va, size_t);
retcode = g_dbgmod.dbg_read_file(fn, off, buf, size);
}
break;
case debugger_t::ev_write_file:
{
int fn = va_arg(va, int);
qoff64_t off = va_arg(va, qoff64_t);
const void *buf = va_arg(va, const void *);
size_t size = va_arg(va, size_t);
retcode = g_dbgmod.dbg_write_file(fn, off, buf, size);
}
break;
case debugger_t::ev_map_address:
{
ea_t *mapped = va_arg(va, ea_t *);
ea_t ea = va_arg(va, ea_t);
const regval_t *regs = va_arg(va, const regval_t *);
int regnum = va_arg(va, int);
*mapped = g_dbgmod.map_address(ea, regs, regnum);
return DRC_OK;
}
break;
#ifdef GET_DEBMOD_EXTS
case debugger_t::ev_get_debmod_extensions:
{
const void **ext = va_arg(va, const void **);
*ext = GET_DEBMOD_EXTS();
retcode = DRC_OK;
}
break;
#endif
#ifdef HAVE_UPDATE_CALL_STACK
case debugger_t::ev_update_call_stack:
{
thid_t tid = va_argi(va, thid_t);
call_stack_t *trace = va_arg(va, call_stack_t *);
retcode = g_dbgmod.dbg_update_call_stack(tid, trace);
}
break;
#endif
#ifdef HAVE_APPCALL
case debugger_t::ev_appcall:
{
ea_t *blob_ea = va_arg(va, ea_t *);
ea_t func_ea = va_arg(va, ea_t);
thid_t tid = va_arg(va, thid_t);
const func_type_data_t *fti = va_arg(va, const func_type_data_t *);
int nargs = va_arg(va, int);
const regobjs_t *regargs = va_arg(va, const regobjs_t *);
relobj_t *stkargs = va_arg(va, relobj_t *);
regobjs_t *retregs = va_arg(va, regobjs_t *);
errbuf = va_arg(va, qstring *);
debug_event_t *event = va_arg(va, debug_event_t *);
int opts = va_arg(va, int);
qnotused(nargs);
*blob_ea = g_dbgmod.dbg_appcall(func_ea, tid, fti->stkargs, regargs, stkargs, retregs, errbuf, event, opts);
retcode = DRC_OK;
}
break;
case debugger_t::ev_cleanup_appcall:
{
thid_t tid = va_argi(va, thid_t);
retcode = g_dbgmod.dbg_cleanup_appcall(tid);
}
break;
#endif
case debugger_t::ev_eval_lowcnd:
{
thid_t tid = va_argi(va, thid_t);
ea_t ea = va_arg(va, ea_t);
errbuf = va_arg(va, qstring *);
retcode = g_dbgmod.dbg_eval_lowcnd(tid, ea, errbuf);
}
break;
case debugger_t::ev_send_ioctl:
{
int fn = va_arg(va, int);
const void *buf = va_arg(va, const void *);
size_t size = va_arg(va, size_t);
void **poutbuf = va_arg(va, void **);
ssize_t *poutsize = va_arg(va, ssize_t *);
retcode = g_dbgmod.handle_ioctl(fn, buf, size, poutbuf, poutsize);
}
break;
case debugger_t::ev_dbg_enable_trace:
{
thid_t tid = va_arg(va, thid_t);
bool enable = va_argi(va, bool);
int trace_flags = va_arg(va, int);
retcode = g_dbgmod.dbg_enable_trace(tid, enable, trace_flags) ? DRC_OK : DRC_NONE;
}
break;
case debugger_t::ev_is_tracing_enabled:
{
thid_t tid = va_arg(va, thid_t);
int tracebit = va_arg(va, int);
retcode = g_dbgmod.dbg_is_tracing_enabled(tid, tracebit) ? DRC_OK : DRC_NONE;
}
break;
case debugger_t::ev_rexec:
{
const char *cmdline = va_arg(va, const char *);
retcode = g_dbgmod.dbg_rexec(cmdline);
}
break;
#ifdef HAVE_GET_SRCINFO_PATH
case debugger_t::ev_get_srcinfo_path:
{
qstring *path = va_arg(va, qstring *);
ea_t base = va_arg(va, ea_t);
bool ok = g_dbgmod.dbg_get_srcinfo_path(path, base);
retcode = ok ? DRC_OK : DRC_NONE;
}
break;
#endif
case debugger_t::ev_bin_search:
{
ea_t *ea = va_arg(va, ea_t *);
ea_t start_ea = va_arg(va, ea_t);
ea_t end_ea = va_arg(va, ea_t);
const compiled_binpat_vec_t *ptns = va_arg(va, const compiled_binpat_vec_t *);
int srch_flags = va_arg(va, int);
errbuf = va_arg(va, qstring *);
if ( ptns != NULL )
retcode = g_dbgmod.dbg_bin_search(ea, start_ea, end_ea, *ptns, srch_flags, errbuf);
}
break;
}
return retcode;
}
//--------------------------------------------------------------------------
// Initialize debugger plugin
static plugmod_t *idaapi init(void)
{
// copy of the definitions from loader.hpp
// we will delete them after improving the debuggers to use PLUGIN_MULTI.
#define PLUGIN_SKIP nullptr
#define PLUGIN_KEEP ((plugmod_t *)2)
if ( init_plugin() )
{
update_idd_registers();
dbg = &debugger;
plugin_inited = true;
return PLUGIN_KEEP;
}
return PLUGIN_SKIP;
}
//--------------------------------------------------------------------------
// Terminate debugger plugin
static void idaapi term(void)
{
if ( plugin_inited )
{
term_plugin();
plugin_inited = false;
}
// we're being unloaded, clear the 'dbg' pointer if it's ours
if ( dbg == &debugger )
dbg = NULL;
}
//--------------------------------------------------------------------------
// The plugin method - usually is not used for debugger plugins
static bool idaapi run(size_t arg)
{
#ifdef HAVE_PLUGIN_RUN
plugin_run(int(arg));
#else
qnotused(arg);
#endif
return true;
}
//--------------------------------------------------------------------------
//
// DEBUGGER DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
#ifdef SET_DBG_OPTIONS
# define S_SET_DBG_OPTIONS s_set_dbg_options
#else
# define S_SET_DBG_OPTIONS NULL
# define SET_DBG_OPTIONS NULL
#endif
#ifndef S_FILETYPE
# define S_FILETYPE 0
#endif
// DBG_HAS_SET_RESUME_MODE must be set before init_debugger.
// typically arm has no single step mechanism, arm64 macOS11 is an exception.
#if TARGET_PROCESSOR == PLFM_ARM && DEBUGGER_ID != DEBUGGER_ID_ARM_MACOS_USER
# define S_DBG_HAS_SET_RESUME_MODE 0
#else
# define S_DBG_HAS_SET_RESUME_MODE DBG_HAS_SET_RESUME_MODE
#endif
#ifndef DEBUGGER_RESMOD
# define DEBUGGER_RESMOD 0
#endif
debugger_t debugger =
{
IDD_INTERFACE_VERSION,
DEBUGGER_NAME,
DEBUGGER_ID,
PROCESSOR_NAME,
DEBUGGER_FLAGS, // flags
DBG_HAS_ATTACH_PROCESS
| DBG_HAS_REQUEST_PAUSE
| DBG_HAS_SET_EXCEPTION_INFO
| DBG_HAS_THREAD_SUSPEND
| DBG_HAS_THREAD_CONTINUE
| S_DBG_HAS_SET_RESUME_MODE
| DBG_HAS_THREAD_GET_SREG_BASE
| DBG_HAS_CHECK_BPT
| DBG_HAS_REXEC, // flags2
REGISTER_CLASSES,
REGISTER_CLASSES_DEFAULT,
REGISTERS,
REGISTERS_SIZE,
MEMORY_PAGE_SIZE,
bpt_code,
sizeof(bpt_code),
S_FILETYPE,
DEBUGGER_RESMOD,
S_SET_DBG_OPTIONS,
idd_notify,
};
//--------------------------------------------------------------------------
//
// PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_HIDE|PLUGIN_DBG, // plugin flags
init, // initialize
term, // terminate. this pointer may be NULL.
run, // invoke plugin
comment, // long comment about the plugin
// it could appear in the status line
// or as a hint
comment, // multiline help about the plugin
wanted_name, // the preferred short name of the plugin
"" // the preferred hotkey to run the plugin
};

View File

@@ -0,0 +1,258 @@
//
// This file is included from other files, do not directly compile it.
// It contains the implementation of debugger plugin callback functions
//
#include <err.h>
#include <name.hpp>
#include <expr.hpp>
#include <segment.hpp>
#include <typeinf.hpp>
//---------------------------------------------------------------------------
//lint -esym(714, rebase_or_warn) not referenced
int rebase_or_warn(ea_t base, ea_t new_base)
{
int code = rebase_program(new_base - base, MSF_FIXONCE);
if ( code != MOVE_SEGM_OK )
{
msg("Failed to rebase program, error code %d\n", code);
warning("IDA failed to rebase the program.\n"
"Most likely it happened because of the debugger\n"
"segments created to reflect the real memory state.\n\n"
"Please stop the debugger and rebase the program manually.\n"
"For that, please select the whole program and\n"
"use Edit, Segments, Rebase program with delta 0x%08a",
new_base - base);
}
return code;
}
//---------------------------------------------------------------------------
void idaapi s_stopped_at_debug_event(thread_name_vec_t *thr_names, bool dlls_added)
{
// Let the debugger module populate the names
g_dbgmod.dbg_stopped_at_debug_event(NULL, dlls_added, thr_names);
if ( dlls_added )
{
#ifndef RPC_CLIENT
// Pass the debug names to the kernel
g_dbgmod.set_debug_names();
#endif
}
}
//--------------------------------------------------------------------------
// This code is compiled for local debuggers (like win32_user.plw)
#ifndef RPC_CLIENT
//--------------------------------------------------------------------------
AS_PRINTF(3,0) ssize_t dvmsg(int code, rpc_engine_t *, const char *format, va_list va)
{
if ( code == 0 )
return vmsg(format, va);
if ( code > 0 )
vwarning(format, va);
else
verror(format, va);
return 0;
}
//--------------------------------------------------------------------------
AS_PRINTF(2,0) void dmsg(rpc_engine_t *rpc, const char *format, va_list va)
{
dvmsg(0, rpc, format, va);
}
//--------------------------------------------------------------------------
AS_PRINTF(2,0) void derror(rpc_engine_t *rpc, const char *format, va_list va)
{
dvmsg(-1, rpc, format, va);
}
//--------------------------------------------------------------------------
AS_PRINTF(2,0) void dwarning(rpc_engine_t *rpc, const char *format, va_list va)
{
dvmsg(1, rpc, format, va);
}
#endif // end of 'local debugger' code
//--------------------------------------------------------------------------
bool lock_begin(void)
{
return true;
}
//--------------------------------------------------------------------------
bool lock_end(void)
{
return true;
}
//--------------------------------------------------------------------------
void report_idc_error(
rpc_engine_t *,
ea_t ea,
error_t code,
ssize_t errval,
const char *errprm)
{
// Copy errval/errprm to the locations expected by qstrerror()
if ( errprm != NULL && errprm != get_error_string(0) )
QPRM(1, errprm);
else if ( code == eOS )
errno = errval;
else
set_error_data(0, errval);
warning("AUTOHIDE NONE\n%a: %s", ea, qstrerror(code));
}
//--------------------------------------------------------------------------
int for_all_debuggers(debmod_visitor_t &v)
{
return v.visit(&g_dbgmod);
}
//--------------------------------------------------------------------------
drc_t idaapi s_write_register(thid_t tid, int reg_idx, const regval_t *value, qstring *errbuf)
{
return g_dbgmod.dbg_write_register(tid, reg_idx, value, errbuf);
}
//--------------------------------------------------------------------------
drc_t idaapi s_read_registers(thid_t tid, int clsmask, regval_t *values, qstring *errbuf)
{
return g_dbgmod.dbg_read_registers(tid, clsmask, values, errbuf);
}
//--------------------------------------------------------------------------
drc_t idaapi s_update_bpts(
int *nbpts,
update_bpt_info_t *bpts,
int nadd,
int ndel,
qstring *errbuf)
{
return g_dbgmod.dbg_update_bpts(nbpts, bpts, nadd, ndel, errbuf);
}
//--------------------------------------------------------------------------
static void update_idd_registers(void)
{
size_t nregs = g_dbgmod.idaregs.nregs();
if ( nregs > 0 )
{
// register classes
debugger.regclasses = g_dbgmod.idaregs.regclasses();
debugger.default_regclasses = 1; // TODO 1 is the general register set
// registers
debugger.nregs = nregs;
debugger.registers = g_dbgmod.idaregs.registers();
}
}
//--------------------------------------------------------------------------
drc_t s_init(uint32_t *flags2, qstring *errbuf)
{
g_dbgmod.debugger_flags = debugger.flags;
drc_t retcode = g_dbgmod.dbg_init(flags2, errbuf);
if ( retcode > DRC_NONE )
update_idd_registers();
return retcode;
}
//--------------------------------------------------------------------------
static drc_t s_attach_process(
pid_t process_id,
int event_id,
int flags,
qstring *errbuf)
{
drc_t retcode = g_dbgmod.dbg_attach_process(process_id, event_id, flags, errbuf);
if ( retcode > DRC_NONE )
update_idd_registers();
return retcode;
}
//--------------------------------------------------------------------------
static drc_t s_start_process(
const char *path,
const char *args,
const char *startdir,
int flags,
const char *input_path,
uint32 input_file_crc32,
qstring *errbuf)
{
drc_t retcode = g_dbgmod.dbg_start_process(path,
args,
startdir,
flags,
input_path,
input_file_crc32,
errbuf);
if ( retcode > DRC_NONE )
update_idd_registers();
return retcode;
}
#ifdef SET_DBG_OPTIONS
//--------------------------------------------------------------------------
// forward declaration
const char *idaapi SET_DBG_OPTIONS(
const char *keyword,
int pri,
int value_type,
const void *value);
//--------------------------------------------------------------------------
static const char *idaapi s_set_dbg_options(
const char *keyword,
int pri,
int value_type,
const void *value)
{
const char *ret = SET_DBG_OPTIONS(keyword, pri, value_type, value);
update_idd_registers();
return ret;
}
#endif
//--------------------------------------------------------------------------
#ifdef REMOTE_DEBUGGER
bool s_open_remote(const char *hostname, int port_number, const char *password, qstring *errbuf)
{
return g_dbgmod.open_remote(hostname, port_number, password, errbuf);
}
drc_t s_close_remote(void)
{
return g_dbgmod.close_remote();
}
#else
bool s_open_remote(const char *, int, const char *, qstring *)
{
return true;
}
drc_t s_close_remote(void)
{
return DRC_OK;
}
#endif
//--------------------------------------------------------------------------
// Local debuggers must call setup_lowcnd_regfuncs() in order to handle
// register read/write requests from low level bpts.
void init_dbg_idcfuncs(bool init)
{
#if !defined(ENABLE_LOWCNDS) \
|| defined(REMOTE_DEBUGGER) \
|| DEBUGGER_ID == DEBUGGER_ID_X86_IA32_BOCHS
qnotused(init);
#else
setup_lowcnd_regfuncs(init ? idc_get_reg_value : NULL,
init ? idc_set_reg_value : NULL);
#endif
}

View File

@@ -0,0 +1,57 @@
#if defined(__NT__) && !defined(__X86__)
//--------------------------------------------------------------------------
static bool path_start_match(const char *s, const char *f)
{
if ( f == NULL || s == NULL || *f == 0 || *s == 0 )
return false;
if ( s == f )
return true;
qstring t1(s);
qstring t2(f);
size_t l1 = t1.length();
size_t l2 = t2.length();
t1.replace("\\", "/");
t2.replace("\\", "/");
if ( t1[l1-1] == '/' )
--l1;
if ( t2[l2-1] == '/' )
--l2;
if ( l1 > l2
|| l1 < l2 && t2[l1] != '/' )
{
return false;
}
return memicmp(t1.c_str(), t2.c_str(), l1) == 0;
}
//--------------------------------------------------------------------------
static void replace_system32(char *path, size_t sz)
{
char spath[MAXSTR];
spath[0] = 0;
GetSystemDirectoryA(spath, sizeof(spath));
if ( spath[0] == 0 || !path_start_match(spath, path) )
return;
char wpath[MAXSTR];
wpath[0] = 0;
GetSystemWow64Directory(wpath, sizeof(wpath));
if ( wpath[0] == 0 || path_start_match(wpath, spath) )
return;
size_t len = strlen(wpath);
if ( wpath[len-1] == '/' || wpath[len-1] == '\\' )
wpath[len-1] = 0;
len = strlen(spath);
if ( spath[len-1] == '/' || spath[len-1] == '\\' )
{
--len;
path[len] = 0;
}
qstring n;
n.sprnt("%s%s", wpath, &path[len]);
qstrncpy(path, n.c_str(), sz);
}
#else
#define replace_system32(PATH, SZ) do {} while ( false )
#endif

View File

@@ -0,0 +1,246 @@
// This file is included in the debugger stub that runs on the computer with IDA
#include <pro.h>
#include <name.hpp>
#include <diskio.hpp>
#include "dbg_rpc_client.h"
#include "dbg_rpc_hlp.h"
#include "debmod.h"
//--------------------------------------------------------------------------
// check and send to the remote server the specified stub
// do it only if its crc does not match the specified crc
// this function runs on the local machine with ida interface
static uchar *sync_stub(const char *fname, uint32 crc, size_t *psize)
{
bool complain = true;
uchar *retval = NULL;
char path[QMAXPATH];
if ( getsysfile(path, sizeof(path), fname, NULL) != NULL )
{
linput_t *li = open_linput(path, false);
if ( li != NULL )
{
int64 size = qlsize(li);
if ( size > 0 )
{
bytevec_t buf;
buf.resize(size);
if ( qlread(li, buf.begin(), size) == size )
{
complain = false;
if ( calc_crc32(0, buf.begin(), size) != crc )
{
*psize = size;
retval = buf.extract();
}
else
{
msg("Kernel debugger stub is up to date...\n");
*psize = 1; // signal ok
}
}
}
close_linput(li);
}
}
if ( complain )
warning("AUTOHIDE NONE\nCould not find/read debugger stub %s", fname);
return retval;
}
//--------------------------------------------------------------------------
dbg_rpc_client_t::dbg_rpc_client_t(idarpc_stream_t *_irs)
: dbg_rpc_engine_t(/*is_client=*/ true),
client_irs(_irs)
{
pending_event.clear_all();
verbose = false;
recv_timeout = RECV_TIMEOUT_PERIOD;
}
//-------------------------------------------------------------------------
void dbg_rpc_client_t::my_update_wait_dialog(
const char *message,
const rpc_packet_t *rp)
{
if ( send_request_data.wait_dialog_displayed )
{
if ( rp->code != send_request_data.code )
replace_wait_box("%s", message);
}
else
{
show_wait_box("%s", message);
send_request_data.wait_dialog_displayed = true;
}
send_request_data.code = rp->code;
}
//--------------------------------------------------------------------------
// requests received from the server.
// here the client handles certain server -> client requests
bytevec_t dbg_rpc_client_t::on_send_request_interrupt(const rpc_packet_t *rp)
{
memory_deserializer_t mmdsr(rp+1, rp->length);
bytevec_t req = prepare_rpc_packet(RPC_OK);
switch ( rp->code )
{
// send_debug_names_to_ida() is thread safe
case RPC_SET_DEBUG_NAMES:
{
my_update_wait_dialog("Downloading Symbols", rp);
int qty = mmdsr.unpack_dd();
ea_t *addrs = OPERATOR_NEW(ea_t, qty);
char **names = OPERATOR_NEW(char *, qty);
qstring name;
ea_t old = 0;
for ( int i=0; i < qty; i++ )
{
adiff_t o2 = mmdsr.unpack_ea64();
if ( mmdsr.unpack_dd() )
o2 = -o2;
old += o2;
addrs[i] = old;
int oldlen = mmdsr.unpack_dd();
QASSERT(1203, oldlen >= 0 && oldlen <= name.length());
// keep the prefix
name.resize(oldlen);
if ( !mmdsr.unpack_str(&name) )
INTERR(1294);
names[i] = qstrdup(name.c_str());
}
int result = send_debug_names_to_ida(addrs, names, qty);
verb(("set_debug_name(qty=%d) => %d\n", qty, result));
req.pack_dd(result);
for ( int i=0; i < qty; i++ )
qfree(names[i]);
delete [] addrs;
delete [] names;
}
break;
// import_dll() is thread safe
case RPC_IMPORT_DLL:
{
my_update_wait_dialog("Importing DLLs", rp);
ea_t base = mmdsr.unpack_ea64();
const char *path = mmdsr.unpack_str();
int n = mmdsr.unpack_dd();
const void *bytes = mmdsr.unpack_obj_inplace(n);
bytevec_t uuid(bytes, n);
int result = import_dll(import_request_t(base, path, uuid));
verb(("import_dll(base=%a, path=%s) => %d\n", base, path, result));
req.pack_dd(result);
}
break;
// send_debug_event_to_ida() is thread safe
case RPC_HANDLE_DEBUG_EVENT:
{
debug_event_t ev;
extract_debug_event(&ev, mmdsr);
int rqflags = mmdsr.unpack_dd();
int code = send_debug_event_to_ida(&ev, rqflags);
req.pack_dd(code);
}
break;
// sync_stub() is thread safe
case RPC_SYNC_STUB:
{
const char *fname = mmdsr.unpack_str();
uint32 crc = mmdsr.unpack_dd();
// security problem: the debugger server should not be able to
// read an arbitrary file on the local computer. therefore we completely
// ignore the file name and use a hardcoded name.
qnotused(fname);
fname = "ida_kdstub.dll";
size_t size = 0;
uchar *contents = sync_stub(fname, crc, &size);
req.pack_dd((uint32)size);
if ( contents != NULL )
{
req.append(contents, size);
qfree(contents);
}
}
break;
// msg/error/warning are thread safe
case RPC_ERROR:
case RPC_MSG:
case RPC_WARNING:
{
const char *str = mmdsr.unpack_str();
if ( *str != '\0' )
{
if ( rp->code == RPC_MSG )
msg("%s", str);
else if ( rp->code == RPC_ERROR )
error("%s", str);
else
warning("%s", str);
}
}
break;
// no external functions are called
case RPC_EVENT:
{
extract_debug_event(&pending_event, mmdsr);
has_pending_event = true;
req = prepare_rpc_packet(RPC_EVOK);
verbev(("got event, storing it and sending RPC_EVOK\n"));
}
break;
// i doubt that this code is used on the client side
// ioctl_handler is NULL
case RPC_IOCTL:
{
int code = handle_ioctl_packet(req, mmdsr.ptr, mmdsr.end);
if ( code != RPC_OK )
return prepare_rpc_packet((uchar)code);
}
break;
// report_idc_error() is thread safe
case RPC_REPORT_IDC_ERROR:
{
ea_t ea = mmdsr.unpack_ea64();
error_t code = mmdsr.unpack_dd();
const char *errprm;
ssize_t errval;
if ( mmdsr.unpack_db() )
{
errprm = mmdsr.unpack_str();
errval = (ssize_t)errprm;
}
else
{
errprm = NULL;
errval = mmdsr.unpack_ea64();
}
report_idc_error(NULL, ea, code, errval, errprm);
}
break;
default:
return prepare_rpc_packet(RPC_UNK);
}
return req;
}
//-------------------------------------------------------------------------
void dbg_rpc_client_t::on_send_request_end(const rpc_packet_t *)
{
if ( send_request_data.wait_dialog_displayed )
hide_wait_box();
send_request_data.reset();
}

View File

@@ -0,0 +1,35 @@
#ifndef __RPC_CLIENT__
#define __RPC_CLIENT__
#include "dbg_rpc_engine.h"
class dbg_rpc_client_t: public dbg_rpc_engine_t
{
protected:
debug_event_t pending_event;
idarpc_stream_t *client_irs;
bool verbose;
struct send_request_data_t
{
uchar code;
bool wait_dialog_displayed;
send_request_data_t() { reset(); }
void reset() { code = uchar(-1); wait_dialog_displayed = false; }
};
send_request_data_t send_request_data;
void my_update_wait_dialog(const char *message, const rpc_packet_t *rp);
virtual bytevec_t on_send_request_interrupt(const rpc_packet_t *rp) override;
virtual void on_send_request_end(const rpc_packet_t *result) override;
public:
dbg_rpc_client_t(idarpc_stream_t *irs);
virtual ~dbg_rpc_client_t() {}
virtual idarpc_stream_t *get_irs() const override { return client_irs; }
};
#endif

View File

@@ -0,0 +1,166 @@
#include <network.hpp>
#include "dbg_rpc_engine.h"
//-------------------------------------------------------------------------
#ifdef TESTABLE_BUILD
static const rpc_packet_type_desc_t dbg_rpc_packet_t_descs[] =
{
{ RPC_OK, "RPC_OK", nullptr },
{ RPC_UNK, "RPC_UNK", nullptr },
{ RPC_MEM, "RPC_MEM", nullptr },
{ RPC_OPEN, "RPC_OPEN", nullptr },
{ RPC_EVENT, "RPC_EVENT", nullptr },
{ RPC_EVOK, "RPC_EVOK", nullptr },
{ RPC_CANCELLED, "RPC_CANCELLED", nullptr },
{ RPC_INIT, "RPC_INIT", nullptr },
{ RPC_TERM, "RPC_TERM", nullptr },
{ RPC_GET_PROCESSES, "RPC_GET_PROCESSES", nullptr },
{ RPC_START_PROCESS, "RPC_START_PROCESS", nullptr },
{ RPC_EXIT_PROCESS, "RPC_EXIT_PROCESS", nullptr },
{ RPC_ATTACH_PROCESS, "RPC_ATTACH_PROCESS", nullptr },
{ RPC_DETACH_PROCESS, "RPC_DETACH_PROCESS", nullptr },
{ RPC_GET_DEBUG_EVENT, "RPC_GET_DEBUG_EVENT", nullptr },
{ RPC_PREPARE_TO_PAUSE_PROCESS, "RPC_PREPARE_TO_PAUSE_PROCESS", nullptr },
{ RPC_STOPPED_AT_DEBUG_EVENT, "RPC_STOPPED_AT_DEBUG_EVENT", nullptr },
{ RPC_CONTINUE_AFTER_EVENT, "RPC_CONTINUE_AFTER_EVENT", nullptr },
{ RPC_TH_SUSPEND, "RPC_TH_SUSPEND", nullptr },
{ RPC_TH_CONTINUE, "RPC_TH_CONTINUE", nullptr },
{ RPC_SET_RESUME_MODE, "RPC_SET_RESUME_MODE", nullptr },
{ RPC_GET_MEMORY_INFO, "RPC_GET_MEMORY_INFO", nullptr },
{ RPC_READ_MEMORY, "RPC_READ_MEMORY", nullptr },
{ RPC_WRITE_MEMORY, "RPC_WRITE_MEMORY", nullptr },
{ RPC_UPDATE_BPTS, "RPC_UPDATE_BPTS", nullptr },
{ RPC_UPDATE_LOWCNDS, "RPC_UPDATE_LOWCNDS", nullptr },
{ RPC_EVAL_LOWCND, "RPC_EVAL_LOWCND", nullptr },
{ RPC_ISOK_BPT, "RPC_ISOK_BPT", nullptr },
{ RPC_READ_REGS, "RPC_READ_REGS", nullptr },
{ RPC_WRITE_REG, "RPC_WRITE_REG", nullptr },
{ RPC_GET_SREG_BASE, "RPC_GET_SREG_BASE", nullptr },
{ RPC_SET_EXCEPTION_INFO, "RPC_SET_EXCEPTION_INFO", nullptr },
{ RPC_OPEN_FILE, "RPC_OPEN_FILE", nullptr },
{ RPC_CLOSE_FILE, "RPC_CLOSE_FILE", nullptr },
{ RPC_READ_FILE, "RPC_READ_FILE", nullptr },
{ RPC_WRITE_FILE, "RPC_WRITE_FILE", nullptr },
{ RPC_IOCTL, "RPC_IOCTL", nullptr },
{ RPC_UPDATE_CALL_STACK, "RPC_UPDATE_CALL_STACK", nullptr },
{ RPC_APPCALL, "RPC_APPCALL", nullptr },
{ RPC_CLEANUP_APPCALL, "RPC_CLEANUP_APPCALL", nullptr },
{ RPC_REXEC, "RPC_REXEC", nullptr },
{ RPC_GET_SCATTERED_IMAGE, "RPC_GET_SCATTERED_IMAGE", nullptr },
{ RPC_GET_IMAGE_UUID, "RPC_GET_IMAGE_UUID", nullptr },
{ RPC_GET_SEGM_START, "RPC_GET_SEGM_START", nullptr },
{ RPC_BIN_SEARCH, "RPC_BIN_SEARCH", nullptr },
{ RPC_SET_DEBUG_NAMES, "RPC_SET_DEBUG_NAMES", nullptr },
{ RPC_SYNC_STUB, "RPC_SYNC_STUB", nullptr },
{ RPC_ERROR, "RPC_ERROR", nullptr },
{ RPC_MSG, "RPC_MSG", nullptr },
{ RPC_WARNING, "RPC_WARNING", nullptr },
{ RPC_HANDLE_DEBUG_EVENT, "RPC_HANDLE_DEBUG_EVENT", nullptr },
{ RPC_REPORT_IDC_ERROR, "RPC_REPORT_IDC_ERROR", nullptr },
{ RPC_IMPORT_DLL, "RPC_IMPORT_DLL", nullptr },
};
#endif
//-------------------------------------------------------------------------
dbg_rpc_engine_t::dbg_rpc_engine_t(bool _is_client)
: rpc_engine_t(_is_client),
has_pending_event(false),
poll_debug_events(false)
{
#ifdef TESTABLE_BUILD
register_packet_type_descs(
dbg_rpc_packet_t_descs,
qnumber(dbg_rpc_packet_t_descs));
#endif
}
//--------------------------------------------------------------------------
// sends a request and waits for a reply
// may occasionally send another request based on the reply
rpc_packet_t *dbg_rpc_engine_t::send_request_and_receive_reply(bytevec_t &req, int flags)
{
bool displayed = false;
rpc_packet_t *result = NULL;
while ( true )
{
if ( displayed && user_cancelled() )
req = prepare_rpc_packet(RPC_CANCELLED);
if ( !req.empty() )
{
int code = send_data(req);
if ( code != 0 || (flags & PREQ_GET_EVENT) != 0 )
break;
rpc_packet_t *reqp = (rpc_packet_t *)req.begin();
if ( reqp->code == RPC_ERROR )
qexit(1); // sent error packet, may die now
}
rpc_packet_t *rp = recv_packet();
if ( rp == NULL )
break;
switch ( rp->code )
{
case RPC_UNK:
dwarning("rpc: remote did not understand our request");
goto FAILURE;
case RPC_MEM:
dwarning("rpc: no remote memory");
goto FAILURE;
case RPC_CANCELLED:
msg("rpc: user cancelled the operation\n");
goto FAILURE;
case RPC_OK:
result = rp;
goto END;
default:
// other replies are passed to the handler
break;
}
if ( !logged_in )
{
lprintf("Exploit packet has been detected and ignored\n");
FAILURE:
qfree(rp);
break;
}
// handle actual command in the request
// FIXME: use a better function name
req = on_send_request_interrupt(rp);
qfree(rp);
}
END:
on_send_request_end(result);
return result;
}
//-------------------------------------------------------------------------
int dbg_rpc_engine_t::_send_request_get_int_result(
bytevec_t &req,
int failure_code,
qstring *errbuf)
{
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return failure_code;
memory_deserializer_t mmdsr(rp+1, rp->length);
int rc = mmdsr.unpack_dd();
if ( rc < 0 && errbuf != NULL )
*errbuf = mmdsr.unpack_str();
qfree(rp);
return rc;
}

View File

@@ -0,0 +1,37 @@
#ifndef __DBG_RPC_ENGINE__
#define __DBG_RPC_ENGINE__
#include <pro.h>
#include <idd.hpp>
#include <network.hpp>
//-------------------------------------------------------------------------
class dbg_rpc_engine_t : public rpc_engine_t
{
public:
bool has_pending_event;
bool poll_debug_events;
dbg_rpc_engine_t(bool _is_client);
#define PREQ_MUST_LOGIN 0x01
#define PREQ_GET_EVENT 0x02
rpc_packet_t *send_request_and_receive_reply(bytevec_t &pkt, int flags);
virtual rpc_packet_t *send_request_and_receive_reply(bytevec_t &pkt) override
{
return send_request_and_receive_reply(pkt, 0);
}
protected:
int send_request_get_long_result(bytevec_t &pkt) { return _send_request_get_int_result(pkt, -1, NULL); }
drc_t send_request_get_drc_result(bytevec_t &pkt, qstring *errbuf) { return drc_t(_send_request_get_int_result(pkt, DRC_NETERR, errbuf)); }
virtual bytevec_t on_send_request_interrupt(const rpc_packet_t *rp) = 0;
virtual void on_send_request_end(const rpc_packet_t *result) newapi { qnotused(result); }
private:
int _send_request_get_int_result(bytevec_t &pkt, int failure_code, qstring *errbuf);
};
#endif // __DBG_RPC_ENGINE__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
#ifndef __DBG_RPC_HANDLER__
#define __DBG_RPC_HANDLER__
#include <network.hpp>
#include "dbg_rpc_engine.h"
#define MIN_SERVER_IOCTL 0x01000000
#include "debmod.h"
struct dbgsrv_dispatcher_t;
//-------------------------------------------------------------------------
class dbg_rpc_handler_t
: public client_handler_t,
public dbg_rpc_engine_t
{
public:
dbg_rpc_handler_t(idarpc_stream_t *_irs, dbgsrv_dispatcher_t *_dispatcher);
virtual ~dbg_rpc_handler_t();
virtual bool handle() override;
virtual void shutdown_gracefully(int signum) override;
private:
debug_event_t ev;
debug_event_t pending_event;
debmod_t *dbg_mod;
dbgsrv_dispatcher_t *dispatcher;
DECLARE_UNCOPYABLE(dbg_rpc_handler_t);
void append_start_or_attach(bytevec_t &req, drc_t drc, const qstring &errbuf) const;
int poll_events(int timeout_ms);
void extract_path_and_arch(
const char **out_file_path,
int *out_arch,
int *out_is_be,
memory_deserializer_t &mmdsr) const;
int on_recv_packet_progress(bool *performed);
protected:
void rpc_update_lowcnds(bytevec_t &rcmd, memory_deserializer_t &mmdsr);
int rpc_update_bpts(bytevec_t &rcmd, memory_deserializer_t &mmdsr);
drc_t rpc_attach_process(qstring *errbuf, memory_deserializer_t &mmdsr);
bool check_broken_connection(pid_t pid);
virtual int handle_server_ioctl(int fn, void **out, ssize_t *outsz, memory_deserializer_t &mmdsr) newapi;
virtual bytevec_t on_send_request_interrupt(const rpc_packet_t *rp) override;
public:
void set_debugger_instance(debmod_t *instance);
debmod_t *get_debugger_instance();
void prepare_broken_connection();
bool rpc_sync_stub(const char *server_stub_name, const char *ida_stub_name);
int send_debug_names_to_ida(ea_t *ea, const char *const *names, int qty);
int send_debug_event_to_ida(const debug_event_t *ev, int rqflags);
void process_import_requests(const import_infos_t &infos);
virtual idarpc_stream_t *get_irs() const override { return irs; }
virtual bool get_broken_connection(void) override;
virtual void set_broken_connection(void) override;
int kill_process(void); // returns 0-ok, >0-failed, after that many seconds
};
// defined only in the single threaded version of the server:
extern dbg_rpc_handler_t *g_global_server;
#endif // __DBG_RPC_HANDLER__

View File

@@ -0,0 +1,70 @@
#ifndef __DBG_RPC_HANDLER_IOCTLS__
#define __DBG_RPC_HANDLER_IOCTLS__
// Note: the dbg_rpc_handler_t implementation will consider all
// IOCTL IDs >= 0x01000000 as being server IOCTLs, and those will
// consequently *not* be passed to the debugger module.
#define MIN_SERVER_IOCTL 0x01000000
#define DWARF_RPCSRV_IOCTL_OK 0
#define DWARF_RPCSRV_IOCTL_ERROR -1
enum rpcsrv_ioctl_t
{
// Get DWARF sections information.
//
// client->server
// (unpacked) char * : file_path (on the server's disk.)
// (packed) uint32 : processor ID (as in: ph.id)
// server->client
// (unpacked) byte : DWARF info found
// (packed) uint32 : is_64 (0 - no, !=0 - yes)
// (packed) uint32 : is_msb (0 - no, !=0 - yes)
// (packed) uint64 : declared load address
// (packed) uint32 : number of DWARF section infos.
// (packed) sec info : DWARF section info, N times.
// Returns: 0 - ok
// !=0 - error (text in output buffer.)
//
// The structure of a "sec info" is:
// (packed) uint64 address_in_memory
// (packed) uint64 size (in bytes)
// (packed) uint16 section_index
// (unpacked) char * section_name
rpcsrv_ioctl_dwarf_secinfo = MIN_SERVER_IOCTL + 1,
// Get DWARF section data.
//
// client->server
// (unpacked) char * : file_path (on the server's disk.)
// (packed) uint32 : processor ID (as in: ph.id)
// (packed) uint16 : DWARF section index (as returned by 'rpcsrv_ioctl_dwarf_secinfo')
// server->client
// (unpacked) byte * : DWARF section data.
// Returns: 0 - ok
// !=0 - error
rpcsrv_ioctl_dwarf_secdata,
#if defined(TESTABLE_BUILD)
// Set path to look for ELF/DWARF companion files, per-PID
//
// This is strictly meant for testing, where tests can store
// files in unusual places on the leasing debug server's volume.
//
// client->server
// (packed) uint32 : the PID
// (unpacked) char * : directory path (on the server's disk.)
// server->client
// Nothing
// Returns: 0 - ok
// != 0 - error
rpcsrv_ioctl_set_elf_debug_file_directory_for_pid = 2 * MIN_SERVER_IOCTL + 1,
#endif
};
#if defined(TESTABLE_BUILD) && defined(__LINUX__)
void set_elf_debug_file_directory_for_pid(int pid, const char *path);
const char *get_elf_debug_file_directory_for_pid(int pid);
#endif
#endif // __DBG_RPC_HANDLER_IOCTLS__

View File

@@ -0,0 +1,367 @@
#include <segment.hpp>
#include <typeinf.hpp>
#include "dbg_rpc_hlp.h"
//--------------------------------------------------------------------------
void append_memory_info(bytevec_t &s, const memory_info_t *meminf)
{
s.pack_ea64(meminf->sbase);
s.pack_ea64(meminf->start_ea - (meminf->sbase << 4));
s.pack_ea64(meminf->size());
s.pack_dd(meminf->perm | (meminf->bitness<<4));
s.pack_str(meminf->name.c_str());
s.pack_str(meminf->sclass.c_str());
}
//--------------------------------------------------------------------------
void extract_memory_info(memory_info_t *meminf, memory_deserializer_t &mmdsr)
{
meminf->sbase = mmdsr.unpack_ea64();
meminf->start_ea = ea_t(meminf->sbase << 4) + mmdsr.unpack_ea64();
meminf->end_ea = meminf->start_ea + mmdsr.unpack_ea64();
int v = mmdsr.unpack_dd();
meminf->perm = uchar(v) & SEGPERM_MAXVAL;
meminf->bitness = uchar(v>>4);
meminf->name = mmdsr.unpack_str();
meminf->sclass = mmdsr.unpack_str();
}
//--------------------------------------------------------------------------
void append_scattered_segm(bytevec_t &s, const scattered_segm_t *ss)
{
s.pack_ea64(ss->start_ea);
s.pack_ea64(ss->end_ea);
s.pack_str(ss->name.c_str());
}
//--------------------------------------------------------------------------
void extract_scattered_segm(scattered_segm_t *ss, memory_deserializer_t &mmdsr)
{
ss->start_ea = mmdsr.unpack_ea64();
ss->end_ea = mmdsr.unpack_ea64();
ss->name = mmdsr.unpack_str();
}
//--------------------------------------------------------------------------
void append_process_info_vec(bytevec_t &s, const procinfo_vec_t *procs)
{
size_t size = procs->size();
s.pack_dd(size);
for ( size_t i = 0; i < size; i++ )
{
const process_info_t &pi = procs->at(i);
s.pack_dd(pi.pid);
s.pack_str(pi.name.c_str());
}
}
//--------------------------------------------------------------------------
void extract_process_info_vec(procinfo_vec_t *procs, memory_deserializer_t &mmdsr)
{
size_t size = mmdsr.unpack_dd();
for ( size_t i = 0; i < size; i++ )
{
process_info_t &pi = procs->push_back();
pi.pid = mmdsr.unpack_dd();
pi.name = mmdsr.unpack_str();
}
}
//--------------------------------------------------------------------------
void append_module_info(bytevec_t &s, const modinfo_t *modinf)
{
s.pack_str(modinf->name);
s.pack_ea64(modinf->base);
s.pack_ea64(modinf->size);
s.pack_ea64(modinf->rebase_to);
}
//--------------------------------------------------------------------------
void extract_module_info(modinfo_t *modinf, memory_deserializer_t &mmdsr)
{
modinf->name = mmdsr.unpack_str();
modinf->base = mmdsr.unpack_ea64();
modinf->size = mmdsr.unpack_ea64();
modinf->rebase_to = mmdsr.unpack_ea64();
}
//--------------------------------------------------------------------------
void append_exception(bytevec_t &s, const excinfo_t *e)
{
s.pack_dd(e->code);
s.pack_dd(e->can_cont);
s.pack_ea64(e->ea);
s.pack_str(e->info);
}
//--------------------------------------------------------------------------
void extract_exception(excinfo_t *exc, memory_deserializer_t &mmdsr)
{
exc->code = mmdsr.unpack_dd();
exc->can_cont = mmdsr.unpack_dd() != 0;
exc->ea = mmdsr.unpack_ea64();
exc->info = mmdsr.unpack_str();
}
//--------------------------------------------------------------------------
void extract_debug_event(debug_event_t *ev, memory_deserializer_t &mmdsr)
{
ev->set_eid(event_id_t(mmdsr.unpack_dd()));
ev->pid = mmdsr.unpack_dd();
ev->tid = mmdsr.unpack_dd();
ev->ea = mmdsr.unpack_ea64();
ev->handled = mmdsr.unpack_dd() != 0;
switch ( ev->eid() )
{
case NO_EVENT: // Not an interesting event
case STEP: // One instruction executed
case PROCESS_DETACHED: // Detached from process
default:
break;
case PROCESS_STARTED: // New process started
case PROCESS_ATTACHED: // Attached to running process
case LIB_LOADED: // New library loaded
extract_module_info(&ev->modinfo(), mmdsr);
break;
case PROCESS_EXITED: // Process stopped
case THREAD_EXITED: // Thread stopped
ev->exit_code() = mmdsr.unpack_dd();
break;
case BREAKPOINT: // Breakpoint reached
extract_breakpoint(&ev->bpt(), mmdsr);
break;
case EXCEPTION: // Exception
extract_exception(&ev->exc(), mmdsr);
break;
case THREAD_STARTED: // New thread started
case LIB_UNLOADED: // Library unloaded
case INFORMATION: // User-defined information
ev->info() = mmdsr.unpack_str();
break;
}
}
//--------------------------------------------------------------------------
void append_debug_event(bytevec_t &s, const debug_event_t *ev)
{
s.pack_dd(ev->eid());
s.pack_dd(ev->pid);
s.pack_dd(ev->tid);
s.pack_ea64(ev->ea);
s.pack_dd(ev->handled);
switch ( ev->eid() )
{
case NO_EVENT: // Not an interesting event
case STEP: // One instruction executed
case PROCESS_DETACHED: // Detached from process
default:
break;
case PROCESS_STARTED: // New process started
case PROCESS_ATTACHED: // Attached to running process
case LIB_LOADED: // New library loaded
append_module_info(s, &ev->modinfo());
break;
case PROCESS_EXITED: // Process stopped
case THREAD_EXITED: // Thread stopped
s.pack_dd(ev->exit_code());
break;
case BREAKPOINT: // Breakpoint reached
append_breakpoint(s, &ev->bpt());
break;
case EXCEPTION: // Exception
append_exception(s, &ev->exc());
break;
case THREAD_STARTED: // New thread started
case LIB_UNLOADED: // Library unloaded
case INFORMATION: // User-defined information
s.pack_str(ev->info());
break;
}
}
//--------------------------------------------------------------------------
exception_info_t *extract_exception_info(int qty, memory_deserializer_t &mmdsr)
{
exception_info_t *extable = NULL;
if ( qty > 0 )
{
extable = OPERATOR_NEW(exception_info_t, qty);
for ( int i=0; i < qty; i++ )
{
extable[i].code = mmdsr.unpack_dd();
extable[i].flags = mmdsr.unpack_dd();
extable[i].name = mmdsr.unpack_str();
extable[i].desc = mmdsr.unpack_str();
}
}
return extable;
}
//--------------------------------------------------------------------------
void append_exception_info(bytevec_t &s, const exception_info_t *table, int qty)
{
for ( int i=0; i < qty; i++ )
{
s.pack_dd(table[i].code);
s.pack_dd(table[i].flags);
s.pack_str(table[i].name.c_str());
s.pack_str(table[i].desc.c_str());
}
}
//--------------------------------------------------------------------------
void extract_call_stack(call_stack_t *trace, memory_deserializer_t &mmdsr)
{
int n = mmdsr.unpack_dd();
trace->resize(n);
for ( int i=0; i < n; i++ )
{
call_stack_info_t &ci = (*trace)[i];
ci.callea = mmdsr.unpack_ea64();
ci.funcea = mmdsr.unpack_ea64();
ci.fp = mmdsr.unpack_ea64();
ci.funcok = mmdsr.unpack_dd() != 0;
}
}
//--------------------------------------------------------------------------
void append_call_stack(bytevec_t &s, const call_stack_t &trace)
{
int n = trace.size();
s.pack_dd(n);
for ( int i=0; i < n; i++ )
{
const call_stack_info_t &ci = trace[i];
s.pack_ea64(ci.callea);
s.pack_ea64(ci.funcea);
s.pack_ea64(ci.fp);
s.pack_dd(ci.funcok);
}
}
//--------------------------------------------------------------------------
void extract_regobjs(
regobjs_t *regargs,
bool with_values,
memory_deserializer_t &mmdsr)
{
int n = mmdsr.unpack_dd();
regargs->resize(n);
for ( int i=0; i < n; i++ )
{
regobj_t &ro = (*regargs)[i];
ro.regidx = mmdsr.unpack_dd();
int size = mmdsr.unpack_dd();
ro.value.resize(size);
if ( with_values )
{
ro.relocate = mmdsr.unpack_dd();
mmdsr.unpack_obj(ro.value.begin(), size);
}
}
}
//--------------------------------------------------------------------------
static void extract_relobj(relobj_t *stkargs, memory_deserializer_t &mmdsr)
{
int n = mmdsr.unpack_dd();
stkargs->resize(n);
mmdsr.unpack_obj(stkargs->begin(), n);
stkargs->base = mmdsr.unpack_ea64();
n = mmdsr.unpack_dd();
stkargs->ri.resize(n);
mmdsr.unpack_obj(stkargs->ri.begin(), n);
}
//--------------------------------------------------------------------------
void extract_appcall(
regobjs_t *regargs,
relobj_t *stkargs,
regobjs_t *retregs,
memory_deserializer_t &mmdsr)
{
extract_regobjs(regargs, true, mmdsr);
extract_relobj(stkargs, mmdsr);
if ( retregs != NULL )
extract_regobjs(retregs, false, mmdsr);
}
//--------------------------------------------------------------------------
void append_regobjs(bytevec_t &s, const regobjs_t &regargs, 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 &regargs,
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);
}

View File

@@ -0,0 +1,80 @@
#ifndef __DBG_RPC_HLP__
#define __DBG_RPC_HLP__
#include <pro.h>
#include <range.hpp>
#include <idd.hpp>
#include <network.hpp>
void append_regvals(bytevec_t &s, const regval_t *values, int n, const uchar *regmap);
void append_debug_event(bytevec_t &s, const debug_event_t *ev);
void extract_debug_event(debug_event_t *ev, memory_deserializer_t &mmdsr);
void extract_exception(excinfo_t *exc, memory_deserializer_t &mmdsr);
void append_exception(bytevec_t &s, const excinfo_t *e);
inline void append_breakpoint(bytevec_t &s, const bptaddr_t *info)
{
s.pack_ea64(info->hea);
s.pack_ea64(info->kea);
}
inline void extract_breakpoint(bptaddr_t *info, memory_deserializer_t &mmdsr)
{
info->hea = mmdsr.unpack_ea64();
info->kea = mmdsr.unpack_ea64();
}
void extract_module_info(modinfo_t *info, memory_deserializer_t &mmdsr);
void append_module_info(bytevec_t &s, const modinfo_t *info);
void extract_process_info_vec(procinfo_vec_t *procs, memory_deserializer_t &mmdsr);
void append_process_info_vec(bytevec_t &s, const procinfo_vec_t *procs);
void extract_call_stack(call_stack_t *trace, memory_deserializer_t &mmdsr);
void append_call_stack(bytevec_t &s, const call_stack_t &trace);
void extract_regobjs(regobjs_t *regargs, bool with_values, memory_deserializer_t &mmdsr);
void append_regobjs(bytevec_t &s, const regobjs_t &regargs, 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 &regargs,
const relobj_t &stkargs,
const regobjs_t *retregs);
void extract_debapp_attrs(
debapp_attrs_t *attrs,
memory_deserializer_t &mmdsr);
void append_debapp_attrs(bytevec_t &s, const debapp_attrs_t &attrs);
void extract_dynamic_register_set(
dynamic_register_set_t *idaregs,
memory_deserializer_t &mmdsr);
void append_dynamic_register_set(
bytevec_t &s,
dynamic_register_set_t &idaregs);
inline void append_type(bytevec_t &s, const type_t *str)
{
s.pack_str((char *)str);
}
void append_type(bytevec_t &s, const tinfo_t &tif);
void extract_type(tinfo_t *tif, memory_deserializer_t &mmdsr);
void extract_memory_info(memory_info_t *info, memory_deserializer_t &mmdsr);
void append_memory_info(bytevec_t &s, const memory_info_t *info);
void extract_scattered_segm(scattered_segm_t *ss, memory_deserializer_t &mmdsr);
void append_scattered_segm(bytevec_t &s, const scattered_segm_t *ss);
void append_exception_info(bytevec_t &s, const exception_info_t *table, int qty);
exception_info_t *extract_exception_info(int qty, memory_deserializer_t &mmdsr);
#endif // __DBG_RPC_HLP__

22
idasdk76/dbg/deb_arm.hpp Normal file
View File

@@ -0,0 +1,22 @@
#ifndef __DEB_ARM__
#define __DEB_ARM__
#include <ua.hpp>
#include <idd.hpp>
#define MEMORY_PAGE_SIZE 0x1000
#define ARM_BPT_CODE { 0xF0, 0x01, 0xF0, 0xE7 } // und #10
#define AARCH64_BPT_CODE { 0x00, 0x00, 0x20, 0xD4 } // brk #0
#define ARM_BPT_SIZE 4 // size of BPT instruction
#define ARM_T 20 // number of virtual T segment register in IDA
// it controls thumb/arm mode.
#define ARM_MAX_HWBPTS 16
#define ARM_MAX_WATCHPOINTS 16
int is_arm_valid_bpt(bpttype_t type, ea_t ea, int len);
#endif

47
idasdk76/dbg/deb_pc.hpp Normal file
View File

@@ -0,0 +1,47 @@
#ifndef __DEB_PC__
#define __DEB_PC__
#include <ua.hpp>
#include <range.hpp>
#include <idd.hpp>
#define MEMORY_PAGE_SIZE 0x1000
#define X86_BPT_CODE { 0xCC }
#define MAX_BPT 4 // maximal number of hardware breakpoints
#define X86_BPT_SIZE 1 // size of 0xCC instruction
#define EFLAGS_TRAP_FLAG 0x00000100
//--------------------------------------------------------------------------
drc_t idaapi x86_read_registers(thid_t thread_id, int clsmask, regval_t *values, qstring *errbuf);
drc_t idaapi x86_write_register(thid_t thread_id, int regidx, const regval_t *value, qstring *errbuf);
int is_x86_valid_bpt(bpttype_t type, ea_t ea, int len);
//--------------------------------------------------------------------------
inline int check_x86_hwbpt(bpttype_t type, ea_t ea, int len)
{
if ( type != BPT_RDWR // type is good?
&& type != BPT_WRITE
&& type != BPT_EXEC )
{
return BPT_BAD_TYPE;
}
if ( len != 1 // is length good?
&& (type == BPT_EXEC // instruction hardware breakpoint only accepts the len of one byte
|| (len != 2 && len != 4
#ifndef __X86__
&& len != 8
#endif
)) )
{
return BPT_BAD_LEN;
}
if ( (ea & (len-1)) != 0 ) // is alignment good?
return BPT_BAD_ALIGN;
return BPT_OK;
}
#endif

1333
idasdk76/dbg/debmod.cpp Normal file

File diff suppressed because it is too large Load Diff

739
idasdk76/dbg/debmod.h Normal file
View File

@@ -0,0 +1,739 @@
#ifndef __DEBUGGER_MODULE__
#define __DEBUGGER_MODULE__
//
//
// This is the base debmod_t class definition
// From this class all debugger code must inherite and specialize
//
// Some OS specific functions must be implemented:
// bool init_subsystem();
// bool term_subsystem();
// debmod_t *create_debug_session(void *);
// int create_thread(thread_cb_t thread_cb, void *context);
//
#include <deque>
#include <pro.h>
#include <idd.hpp>
#include <network.hpp>
//--------------------------------------------------------------------------
extern debugger_t debugger;
//--------------------------------------------------------------------------
struct name_info_t
{
eavec_t addrs;
qvector<char *> names;
void clear(void)
{
addrs.clear();
names.clear();
}
};
//--------------------------------------------------------------------------
// Extended process info
struct ext_process_info_t : public process_info_t
{
int addrsize; // process bitness 32bit - 4, 64bit - 8, 0 - unknown
qstring ext_name; // human-readable name (e.g. with command line agrs)
void copy_to(process_info_t *dst)
{
dst->pid = pid;
dst->name = ext_name.empty() ? name : ext_name;
}
};
typedef qvector<ext_process_info_t> procvec_t;
//--------------------------------------------------------------------------
// Very simple class to store pending events
enum queue_pos_t
{
IN_FRONT,
IN_BACK
};
//--------------------------------------------------------------------------
struct pagebpt_data_t
{
ea_t ea; // address of the bpt as specified by the user
ea_t page_ea; // real address of the bpt as written to the process
int user_len; // breakpoint length as specified by the user
int aligned_len; // breakpoint length aligned to the page size
int real_len; // real length of the breakpoint as written to the process
uint32 old_prot; // old page protections (before writing the bpt to the process)
// if 0, the bpt has not been written to the process yet.
uint32 new_prot; // new page protections (when the bpt is active)
bpttype_t type; // breakpoint type
};
// Information about page breakpoints is stored in this data structure.
// The map is indexed by the page start address (not the address specified
// by the user!)
typedef std::map<ea_t, pagebpt_data_t> page_bpts_t; // page_ea -> bpt info
typedef qvector<page_bpts_t::iterator> pbpt_iterators_t; // list of iterators into page_bpts_t
//--------------------------------------------------------------------------
// set of addresses
typedef std::set<ea_t> easet_t;
//-------------------------------------------------------------------------
class idc_value_t;
class rpc_engine_t;
error_t idaapi idc_get_reg_value(idc_value_t *argv, idc_value_t *r);
error_t idaapi idc_set_reg_value(idc_value_t *argv, idc_value_t *r);
void report_idc_error(rpc_engine_t *rpc, ea_t ea, error_t code, ssize_t errval, const char *errprm);
// IDC function name that is exported by a debugger module
// to allow scripts to send debugger commands
#define IDC_SENDDBG_CMD "send_dbg_command"
#define IDC_READ_MSR "read_msr"
#define IDC_WRITE_MSR "write_msr"
#define IDC_STEP_BACK "step_back"
#define IDC_SET_TEV "set_current_tev"
#define IDC_GET_TEV "get_current_tev"
// A macro to convert a pointer to ea_t without sign extension.
#define EA_T(ptr) (ea_t)(size_t)(ptr)
//--------------------------------------------------------------------------
//-V:debmod_bpt_t:730 not all members of a class are initialized inside the constructor: saved
struct debmod_bpt_t
{
ea_t ea;
uchar saved[8]; // size of the biggest supported bpt size (PPC64)
uchar nsaved;
int bid; // (epoc) breakpoint id (from TRK)
debmod_bpt_t() : ea(BADADDR), nsaved(0), bid(0) {}
debmod_bpt_t(ea_t _ea, uchar _nsaved) : ea(_ea), nsaved(_nsaved), bid(0) { QASSERT(1796, nsaved < sizeof(saved)); }
};
typedef std::map<ea_t, debmod_bpt_t> debmodbpt_map_t;
//--------------------------------------------------------------------------
struct eventlist_t : public std::deque<debug_event_t>
{
public:
// save a pending event
void enqueue(const debug_event_t &ev, queue_pos_t pos)
{
if ( pos != IN_BACK )
push_front(ev);
else
push_back(ev);
}
// retrieve a pending event
bool retrieve(debug_event_t *event)
{
if ( empty() )
return false;
// get the first event and return it
*event = front();
pop_front();
return true;
}
};
//--------------------------------------------------------------------------
int send_ioctl(rpc_engine_t *rpc, int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize);
int send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty);
int send_debug_event_to_ida(const debug_event_t *ev, int rqflags);
void set_arm_thumb_modes(ea_t *addrs, int qty);
char *debug_event_str(const debug_event_t *ev, char *buf, size_t bufsize);
char *debug_event_str(const debug_event_t *ev); // returns static buf
//--------------------------------------------------------------------------
// describes a dll to be imported
struct import_request_t
{
ea_t base;
qstring path;
bytevec_t uuid;
import_request_t(ea_t _base, const qstring &_path, const bytevec_t &_uuid)
: base(_base), path(_path), uuid(_uuid) {}
};
DECLARE_TYPE_AS_MOVABLE(import_request_t);
typedef qvector<import_request_t> import_infos_t;
int import_dll(const import_request_t &req);
//--------------------------------------------------------------------------
struct regctx_t;
//--------------------------------------------------------------------------
struct regctx_entry_t
{
enum type_t
{
IVAL = 0,
FVAL = 1,
DATA = 2,
FUNC = 3,
} type;
int reg_class;
int reg_idx;
union
{
struct
{
size_t offset_in_ctx;
size_t size_in_ctx;
size_t reg_size;
};
struct
{
void (*read_func)(const regctx_t *, regval_t *, void *);
void (*write_func)(regctx_t *, const regval_t *, void *);
void *user_data;
};
};
uint64_t truncate_ival(uint64_t ival) const
{
switch ( reg_size )
{
case 1: ival = uint8_t(ival); break;
case 2: ival = uint16_t(ival); break;
case 4: ival = uint32_t(ival); break;
}
return ival;
}
void read(regval_t *value, const uint8_t *ptr) const
{
if ( type == regctx_entry_t::FUNC )
{
read_func((regctx_t *) ptr, value, user_data);
}
else
{
const uint8_t *p = ptr + offset_in_ctx;
switch ( type )
{
case regctx_entry_t::IVAL:
{
uint64_t ival = 0;
switch ( size_in_ctx )
{
case 1: ival = *(uint8_t *)p; break;
case 2: ival = *(uint16_t *)p; break;
case 4: ival = *(uint32_t *)p; break;
case 8: ival = *(uint64_t *)p; break;
}
ival = truncate_ival(ival);
value->ival = ival;
}
break;
case regctx_entry_t::FVAL:
// TODO: for x86, the value is converted to IEEE format in
// x86_read_registers(). clean this up somehow.
memcpy(&value->fval, p, size_in_ctx);
value->rvtype = RVT_FLOAT;
break;
case regctx_entry_t::DATA:
value->set_bytes(p, size_in_ctx);
break;
case regctx_entry_t::FUNC:
// never happens; makes compiler happy.
break;
}
}
}
bool patch(uint8_t *ptr, const regval_t *value) const
{
if ( type == regctx_entry_t::FUNC )
{
write_func((regctx_t *) ptr, value, user_data);
}
else
{
uint8_t *p = ptr + offset_in_ctx;
switch ( type )
{
case regctx_entry_t::IVAL:
{
uint64_t ival = value->ival;
ival = truncate_ival(ival);
switch ( size_in_ctx )
{
case 1: *(uint8_t *)p = ival; break;
case 2: *(uint16_t *)p = ival; break;
case 4: *(uint32_t *)p = ival; break;
case 8: *(uint64_t *)p = ival; break;
}
}
break;
case regctx_entry_t::FVAL:
// TODO: for x86, the value is converted from IEEE format in
// x86_write_register(). clean this up somehow.
memcpy(p, &value->fval, size_in_ctx);
break;
case regctx_entry_t::DATA:
if ( value->get_data_size() != size_in_ctx )
return false;
memcpy(p, value->get_data(), value->get_data_size());
break;
case regctx_entry_t::FUNC:
// never happens; makes compiler happy.
break;
}
}
return true;
}
};
DECLARE_TYPE_AS_MOVABLE(regctx_entry_t);
typedef qvector<regctx_entry_t> reg_ctx_entries_t;
//--------------------------------------------------------------------------
struct regctx_base_t
{
dynamic_register_set_t &idaregs; // linked to debmod_t's variable of the same name
thid_t tid;
int clsmask;
reg_ctx_entries_t entries;
void setup(thid_t _tid, int _clsmask=0)
{
tid = _tid;
clsmask = _clsmask;
}
void setup_reg(int dyn_reg_idx)
{
clsmask |= entries[dyn_reg_idx].reg_class;
}
regctx_base_t(dynamic_register_set_t &_idaregs)
: idaregs(_idaregs)
{
setup(0, 0);
}
void add_idareg(register_info_t &ri)
{
idaregs.add_register(ri.name,
ri.flags,
ri.dtype,
ri.register_class,
ri.bit_strings,
ri.default_bit_strings_mask);
}
size_t add_entry(
regctx_entry_t::type_t type,
register_info_t &ri,
size_t offset_in_ctx,
size_t size_in_ctx,
size_t reg_size)
{
size_t dyn_reg_idx = entries.size();
regctx_entry_t &entry = entries.push_back();
entry.type = type;
entry.reg_class = ri.register_class;
entry.reg_idx = dyn_reg_idx;
entry.offset_in_ctx = offset_in_ctx;
entry.size_in_ctx = size_in_ctx;
entry.reg_size = reg_size;
add_idareg(ri);
return dyn_reg_idx;
}
size_t add_ival(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
{
QASSERT(1797, size_in_ctx <= sizeof(regval_t::ival));
size_t reg_size = ri.dtype == dt_word ? 2
: ri.dtype == dt_dword ? 4
: ri.dtype == dt_qword ? 8
: 0;
QASSERT(1798, reg_size != 0);
return add_entry(regctx_entry_t::IVAL, ri, offset_in_ctx, size_in_ctx, reg_size);
}
size_t add_fval(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
{
QASSERT(1799, size_in_ctx <= sizeof(regval_t::fval));
return add_entry(regctx_entry_t::FVAL, ri, offset_in_ctx, size_in_ctx, size_in_ctx);
}
size_t add_data(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
{
return add_entry(regctx_entry_t::DATA, ri, offset_in_ctx, size_in_ctx, size_in_ctx);
}
size_t add_func(
register_info_t &ri,
void (*read_func)(const regctx_t *, regval_t *, void *),
void (*write_func)(regctx_t *, const regval_t *, void *),
void *user_data=nullptr)
{
size_t dyn_reg_idx = entries.size();
regctx_entry_t &entry = entries.push_back();
entry.type = regctx_entry_t::FUNC;
entry.reg_class = ri.register_class;
entry.reg_idx = dyn_reg_idx;
entry.read_func = read_func;
entry.write_func = write_func;
entry.user_data = user_data;
add_idareg(ri);
return dyn_reg_idx;
}
void read_all(regval_t *values)
{
for ( const regctx_entry_t &entry: entries )
if ( (clsmask & entry.reg_class) != 0 )
entry.read(&values[entry.reg_idx], (const uint8_t *) this);
}
bool patch(int dyn_reg_idx, const regval_t *value)
{
return entries[dyn_reg_idx].patch((uint8_t *) this, value);
}
};
//--------------------------------------------------------------------------
// Main class to represent a debugger module
class debmod_t
{
protected:
// processor_t &ph;
typedef std::map<int, regval_t> regval_map_t;
qvector<exception_info_t> exceptions;
name_info_t dn_names;
// Pending events. currently used only to store
// exceptions that happen while attaching
eventlist_t events;
// The last event received via a successful get_debug_event()
debug_event_t last_event;
// debugged process attributes (may be changed after process start/attach)
debapp_attrs_t debapp_attrs;
procvec_t proclist;
// appcall contexts
struct call_context_t
{
regvals_t saved_regs;
ea_t sp;
ea_t ctrl_ea;
bool regs_spoiled;
call_context_t() : sp(BADADDR), ctrl_ea(BADADDR), regs_spoiled(false) {}
};
typedef qstack<call_context_t> call_contexts_t;
typedef std::map<thid_t, call_contexts_t> appcalls_t;
appcalls_t appcalls;
int send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize)
{
return ::send_ioctl(rpc, fn, buf, size, poutbuf, poutsize);
}
// If an IDC error occurs: we cannot prepare an error message on the server
// side because we do not have access to error strings (they are in ida.hlp).
// We pass the error code to IDA (with eventual arguments) so it can prepare
// a nice error message for the user
void report_idc_error(ea_t ea, error_t code, ssize_t errval, const char *errprm)
{
return ::report_idc_error(rpc, ea, code, errval, errprm);
}
typedef std::map<ea_t, lowcnd_t> lowcnds_t;
lowcnds_t cndmap;
eavec_t handling_lowcnds;
bool evaluate_and_handle_lowcnd(debug_event_t *event, int elc_flags=0);
bool handle_lowcnd(lowcnd_t *lc, debug_event_t *event, int elc_flags);
#define ELC_KEEP_EIP 0x0001 // do not reset eip before stepping
#define ELC_KEEP_SUSP 0x0002 // keep suspended state, do not resume after stepping
// helper functions for programmatical single stepping
virtual drc_t dbg_perform_single_step(debug_event_t *event, const insn_t &ins);
virtual int dbg_freeze_threads_except(thid_t) { return 0; }
virtual int dbg_thaw_threads_except(thid_t) { return 0; }
drc_t resume_app_and_get_event(debug_event_t *dev);
void set_platform(const char *platform_name);
// return number of processes, -1 - not implemented
virtual int idaapi get_process_list(procvec_t *proclist, qstring *errbuf);
public:
// initialized by dbg_init()
int debugger_flags;
meminfo_vec_t old_ranges;
rpc_engine_t *rpc;
bool debug_debugger;
// Is dynamic library?
bool is_dll;
// indexes of sp and program counter registers.
// Must be initialized by derived classes.
int sp_idx;
int pc_idx;
// index of frame pointer register
int fp_idx;
// Total number of registers.
// Must be initialized by derived classes.
int nregs;
// Breakpoint code.
// Must be initialized by derived classes.
bytevec_t bpt_code;
qstring input_file_path;
page_bpts_t page_bpts;
// will either send as msg/warning/error through callui,
// or through 'rpc' if it is present.
DEFINE_ALL_NOTIFICATION_FUNCTIONS(rpc);
AS_PRINTF(3,0) static ssize_t dvnotif(int code, rpc_engine_t *rpc, const char *format, va_list va);
bool broken_connection;
pid_t pid;
debmodbpt_map_t bpts;
update_bpt_vec_t deleted_bpts; // deleted bpts in the last update_bpts() call
// if bpt EA was deleted then bpt raised in the same place for the
// same thread does not belong to IDA
bool is_ida_bpt(ea_t ea, thid_t tid)
{
if ( bpts.find(ea) != bpts.end() )
return true;
update_bpt_info_t b;
b.ea = ea;
return deleted_bpts.find(b) != deleted_bpts.end()
&& (last_event.eid() != BREAKPOINT
|| last_event.ea != ea
|| last_event.tid != tid);
}
static bool reuse_broken_connections;
//------------------------------------
// Dynamic register set for debugger_t
//------------------------------------
dynamic_register_set_t idaregs;
//------------------------------------
// Constructors and destructors
//------------------------------------
debmod_t();
virtual ~debmod_t() { cleanup(); }
//------------------------------------
// Debug names methods
//------------------------------------
void clear_debug_names();
name_info_t *get_debug_names();
void save_debug_name(ea_t ea, const char *name);
int set_debug_names();
int send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty);
int send_debug_event_to_ida(const debug_event_t *ev, int rqflags);
ea_t find_debug_name(const char *name) const;
//------------------------------------
// Utility methods
//------------------------------------
void cleanup(void);
AS_PRINTF(2, 3) void debdeb(const char *format, ...);
AS_PRINTF(2, 3) bool deberr(const char *format, ...);
bool same_as_oldmemcfg(const meminfo_vec_t &ranges) const;
void save_oldmemcfg(const meminfo_vec_t &ranges);
bool continue_after_last_event(bool handled = true);
lowcnd_t *get_failed_lowcnd(thid_t tid, ea_t ea);
page_bpts_t::iterator find_page_bpt(ea_t ea, int size=1);
bool del_page_bpt(ea_t ea, bpttype_t type);
void enable_page_bpts(bool enable);
ea_t calc_page_base(ea_t ea) { return align_down(ea, dbg_memory_page_size()); }
void log_exception(const debug_event_t *ev, const exception_info_t *ei);
uint64 probe_file_size(int fn, uint64 step);
void set_input_path(const char *input_path);
int read_bpt_orgbytes(bytevec_t *buf, ea_t ea, int len);
//------------------------------------
// Shared methods
//------------------------------------
virtual bool check_input_file_crc32(uint32 orig_crc);
virtual const exception_info_t *find_exception(int code);
virtual bool get_exception_name(int code, char *buf, size_t bufsize);
virtual drc_t idaapi dbg_get_processes(procinfo_vec_t *info, qstring *errbuf);
//------------------------------------
// Methods to be implemented
//------------------------------------
virtual void idaapi dbg_set_debugging(bool _debug_debugger) = 0;
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) = 0;
virtual void idaapi dbg_term(void) = 0;
virtual drc_t idaapi dbg_detach_process(void) = 0;
virtual drc_t idaapi dbg_start_process(
const char *path,
const char *args,
const char *startdir,
int flags,
const char *input_path,
uint32 input_file_crc32,
qstring *errbuf) = 0;
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_msecs) = 0;
virtual drc_t idaapi dbg_attach_process(pid_t process_id, int event_id, int flags, qstring *errbuf) = 0;
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) = 0;
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) = 0;
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) = 0;
virtual void idaapi dbg_set_exception_info(const exception_info_t *info, int qty);
virtual void idaapi dbg_stopped_at_debug_event(import_infos_t *infos, bool dlls_added, thread_name_vec_t *thr_names) = 0;
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) = 0;
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) = 0;
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) = 0;
virtual drc_t idaapi dbg_read_registers(
thid_t thread_id,
int clsmask,
regval_t *values,
qstring *errbuf) = 0;
virtual drc_t idaapi dbg_write_register(
thid_t thread_id,
int reg_idx,
const regval_t *value,
qstring *errbuf) = 0;
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) = 0;
virtual ea_t idaapi map_address(ea_t ea, const regval_t *, int /* regnum */) { return ea; }
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &ranges, qstring *errbuf) = 0;
virtual int idaapi dbg_get_scattered_image(scattered_image_t & /*si*/, ea_t /*base*/) { return -1; }
virtual bool idaapi dbg_get_image_uuid(bytevec_t * /*uuid*/, ea_t /*base*/) { return false; }
virtual ea_t idaapi dbg_get_segm_start(ea_t /*base*/, const qstring & /*segname*/) { return BADADDR; }
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) = 0;
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) = 0;
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) = 0;
// for swbpts, len may be -1 (unknown size, for example arm/thumb mode) or bpt opcode length
// dbg_add_bpt returns 2 if it adds a page bpt
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) = 0;
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) = 0;
virtual drc_t idaapi dbg_update_bpts(int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf);
virtual int idaapi dbg_add_page_bpt(bpttype_t /*type*/, ea_t /*ea*/, int /*size*/) { return 0; }
virtual bool idaapi dbg_enable_page_bpt(page_bpts_t::iterator /*p*/, bool /*enable*/) { return false; }
virtual drc_t idaapi dbg_update_lowcnds(int *nupdated, const lowcnd_t *lowcnds, int nlowcnds, qstring *errbuf);
virtual drc_t idaapi dbg_eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf);
virtual int idaapi dbg_open_file(const char * /*file*/, uint64 * /*fsize*/, bool /*readonly*/) { return -1; }
virtual void idaapi dbg_close_file(int /*fn*/) {}
virtual ssize_t idaapi dbg_read_file(int /*fn*/, qoff64_t /*off*/, void * /*buf*/, size_t /*size*/) { return 0; }
virtual ssize_t idaapi dbg_write_file(int /*fn*/, qoff64_t /*off*/, const void * /*buf*/, size_t /*size*/) { return 0; }
virtual int idaapi handle_ioctl(
int /*fn*/,
const void * /*buf*/,
size_t /*size*/,
void ** /*outbuf*/,
ssize_t * /*outsize*/)
{
return 0;
}
virtual int idaapi get_system_specific_errno(void) const; // this code must be acceptable by winerr()
virtual drc_t idaapi dbg_update_call_stack(thid_t, call_stack_t *) { return DRC_NONE; }
virtual ea_t idaapi dbg_appcall(
ea_t /*func_ea*/,
thid_t /*tid*/,
int /*stkarg_nbytes*/,
const struct regobjs_t * /*regargs*/,
struct relobj_t * /*stkargs*/,
struct regobjs_t * /*retregs*/,
qstring *errbuf,
debug_event_t * /*event*/,
int /*flags*/);
virtual drc_t idaapi dbg_cleanup_appcall(thid_t /*tid*/);
virtual bool idaapi write_registers(
thid_t /*tid*/,
int /*start*/,
int /*count*/,
const regval_t * /*values*/)
{
return false;
}
// finalize appcall stack image
// input: stack image contains the return address at the beginning
virtual int finalize_appcall_stack(call_context_t &, regval_map_t &, bytevec_t &) { return 0; }
virtual ea_t calc_appcall_stack(const regvals_t &regvals);
virtual bool should_stop_appcall(thid_t tid, const debug_event_t *event, ea_t ea);
virtual bool should_suspend_at_exception(const debug_event_t *event, const exception_info_t *ei);
virtual bool preprocess_appcall_cleanup(thid_t, call_context_t &) { return true; }
virtual int get_regidx(const char *regname, int *clsmask) = 0;
virtual uint32 dbg_memory_page_size(void) { return 0x1000; }
virtual bool idaapi dbg_prepare_broken_connection(void) { return false; }
virtual bool idaapi dbg_continue_broken_connection(pid_t) { old_ranges.clear(); return true; }
virtual bool idaapi dbg_enable_trace(thid_t, bool, int) { return false; }
virtual bool idaapi dbg_is_tracing_enabled(thid_t, int) { return false; }
virtual int idaapi dbg_rexec(const char *cmdline);
virtual void adjust_swbpt(ea_t *, int *) {}
virtual void dbg_get_debapp_attrs(debapp_attrs_t *out_pattrs) const;
virtual bool idaapi dbg_get_srcinfo_path(qstring * /*path*/, ea_t /*base*/) const { return false; }
virtual bool import_dll(const import_request_t & /*req*/) { return false; }
virtual drc_t idaapi dbg_bin_search(
ea_t *ea,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring *errbuf);
bool restore_broken_breakpoints(void);
void set_addr_size(int size) { debapp_attrs.addrsize = size; }
void set_endianness(bool is_be) { debapp_attrs.is_be = is_be; }
};
//---------------------------------------------------------------------------
//
// Some functions, per OS implemented
//
bool init_subsystem();
bool term_subsystem();
debmod_t *create_debug_session(void *params);
//
// Processor specific init/term
//
void processor_specific_init(void);
void processor_specific_term(void);
// Perform an action on all existing debugger modules
struct debmod_visitor_t
{
virtual int visit(debmod_t *debmod) = 0;
};
int for_all_debuggers(debmod_visitor_t &v);
//
// Utility functions
//
// Common method between MacOS and Linux to launch a process
drc_t idaapi maclnx_launch_process(
debmod_t *debmod,
const char *path,
const char *args,
const char *startdir,
int flags,
const char *input_path,
uint32 input_file_crc32,
void **child_pid,
qstring *errbuf);
bool add_idc_funcs(const struct ext_idcfunc_t funcs[], size_t nfuncs, bool reg);
//
// Externs
//
extern debmod_t *idc_debmod;
extern thid_t idc_thread;
extern bool ignore_sigint;
//---------------------------------------------------------------------------
// server.cpp
bool lock_begin();
bool lock_end();
// bool srv_lock_begin(void);
// bool srv_lock_end(void);
#endif

292
idasdk76/dbg/exceptions.cfg Normal file
View File

@@ -0,0 +1,292 @@
; This file describes the exception definitions.
; The definitions will be used for new databases.
; for each exception:
; code stop_at pass_to_app notification name description
;
; code is the exception code
; stop_at is one of stop or nostop - should the debugger suspend
; the process when the exception occurs
; pass_to_app is one of app or mask - should the debugger pass
; the exception to the application or not
; notification is one of warn/log/silent
; name is the exception short name
; description is the exception description displayed to the user
; Windows exceptions
.win32,bochs_win32
0xC0000005 stop mask warn EXCEPTION_ACCESS_VIOLATION The instruction at 0x%a referenced memory at 0x%a. The memory could not be %s
0x80000002 stop mask warn EXCEPTION_DATATYPE_MISALIGNMENT A datatype misalignment error was detected in a load or store instruction
0x80000003 stop mask warn EXCEPTION_BREAKPOINT Software breakpoint exception
0x80000004 stop mask warn EXCEPTION_SINGLE_STEP Single step exception
0xC000008C stop mask warn EXCEPTION_ARRAY_BOUNDS_EXCEEDED Array bounds exceeded
0xC000008D stop mask warn EXCEPTION_FLT_DENORMAL_OPERAND Floating point denormal operand
0xC000008E stop mask warn EXCEPTION_FLT_DIVIDE_BY_ZERO Floating point divide by zero
0xC000008F stop mask warn EXCEPTION_FLT_INEXACT_RESULT Floating point inexact result
0xC0000090 stop mask warn EXCEPTION_FLT_INVALID_OPERATION Floating point invalid operation
0xC0000091 stop mask warn EXCEPTION_FLT_OVERFLOW Floating point overflow
0xC0000092 stop mask warn EXCEPTION_FLT_STACK_CHECK Floating point stack check
0xC0000093 stop mask warn EXCEPTION_FLT_UNDERFLOW Floating point underflow
0xC0000094 stop mask warn EXCEPTION_INT_DIVIDE_BY_ZERO Integer divide by zero
0xC0000095 stop mask warn EXCEPTION_INT_OVERFLOW Integer overflow
0xC0000096 stop mask warn EXCEPTION_PRIV_INSTRUCTION Priveleged instruction
0xC0000006 stop mask warn EXCEPTION_IN_PAGE_ERROR The instruction at "0x%a" referenced memory at "0x%a". The required data was not placed into memory because of an I/O error status of "0x%a"
0xC000001D stop mask warn EXCEPTION_ILLEGAL_INSTRUCTION An attempt was made to execute an illegal instruction
0xC0000025 stop mask warn EXCEPTION_NONCONTINUABLE_EXCEPTION Windows cannot continue from this exception
0xC00000FD stop mask warn EXCEPTION_STACK_OVERFLOW A new guard page for the stack cannot be created (stack overflow)
0xC0000026 stop mask warn EXCEPTION_INVALID_DISPOSITION An invalid exception disposition was returned by an exception handler
0x80000001 stop mask warn EXCEPTION_GUARD_PAGE A page of memory that marks the end of a data structure such as a stack or an array has been accessed
0xC0000008 stop mask warn EXCEPTION_INVALID_HANDLE An invalid HANDLE was specified
0xEEFFACE stop mask warn EXCEPTION_BCC_FATAL Fatal unhandled exception in the BCC compiled program
0xEEDFAE6 stop mask warn EXCEPTION_BCC_NORMAL Unhandled exception in the BCC compiled program
0x40010005 stop mask warn DBG_CONTROL_C CTRL+C was input to console process
0x40010008 stop mask warn DBG_CONTROL_BREAK CTRL+BREAK was input to console process
0xE06D7363 stop mask warn EXCEPTION_MSC_CPLUSPLUS Microsoft C++ exception
0xE0434F4D stop mask warn EXCEPTION_MANAGED_NET Managed .NET exception
0xE0434352 stop mask warn EXCEPTION_MANAGED_NET_V4 Managed .NET exception (V4+)
0x4000001E stop mask warn EXCEPTION_WX86_SINGLE_STEP Single step exception (x86 emulation)
0x4000001F stop mask warn EXCEPTION_WX86_BREAKPOINT Software breakpoint exception (x86 emulation)
0x406D1388 nostop mask log MS_VC_EXCEPTION SetThreadName
; Linux exceptions
.linux
1 stop mask warn SIGHUP Hangup
2 stop mask warn SIGINT Interrupt
3 stop mask warn SIGQUIT Quit
4 stop mask warn SIGILL Illegal instruction
5 stop mask warn SIGTRAP Trace trap
6 stop mask warn SIGABRT Abort
7 stop mask warn SIGBUS BUS error
8 stop mask warn SIGFPE Floating-point exception
9 stop mask warn SIGKILL Kill unblockable
10 stop mask warn SIGUSR1 User-defined signal 1
11 stop mask warn SIGSEGV Segmentation violation
12 stop mask warn SIGUSR2 User-defined signal 2
13 stop mask warn SIGPIPE Broken pipe
14 stop mask warn SIGALRM Alarm clock
15 stop mask warn SIGTERM Termination
16 stop mask warn SIGSTKFLT Stack fault
17 stop mask warn SIGCHLD Child status has changed
18 stop mask warn SIGCONT Continue
19 stop mask warn SIGSTOP Stop unblockable
20 stop mask warn SIGTSTP Keyboard stop
21 stop mask warn SIGTTIN Background read from tty
22 stop mask warn SIGTTOU Background write to tty
23 stop mask warn SIGURG Urgent condition on socket
24 stop mask warn SIGXCPU CPU limit exceeded
25 stop mask warn SIGXFSZ File size limit exceeded
26 stop mask warn SIGVTALRM Virtual alarm clock
27 stop mask warn SIGPROF Profiling alarm clock
28 stop mask warn SIGWINCH Window size change
29 stop mask warn SIGIO I/O now possible
30 stop mask warn SIGPWR Power failure restart
31 stop mask warn SIGSYS Bad system call
; Mac OS X/iphone exceptions
.macosx,ios,xnu
1 stop mask warn SIGHUP terminal line hangup
2 stop mask warn SIGINT interrupt program
3 stop mask warn SIGQUIT quit program
4 stop mask warn SIGILL illegal instruction
5 stop mask warn SIGTRAP trace trap
6 stop mask warn SIGABRT abort program
7 stop mask warn SIGEMT emulate instruction executed
8 stop mask warn SIGFPE floating-point exception
9 stop mask warn SIGKILL kill program
10 stop mask warn SIGBUS bus error
11 stop mask warn SIGSEGV segmentation violation
12 stop mask warn SIGSYS non-existent system call invoked
13 stop mask warn SIGPIPE write on a pipe with no reader
14 stop mask warn SIGALRM real-time timer expired
15 stop mask warn SIGTERM software termination signal
16 stop mask warn SIGURG urgent condition present on socket
17 stop mask warn SIGSTOP stop
18 stop mask warn SIGTSTP stop signal generated from keyboard
19 stop mask warn SIGCONT continue after stop
20 stop mask warn SIGCHLD child status has changed
21 stop mask warn SIGTTIN background read attempted from control terminal
22 stop mask warn SIGTTOU background write attempted to control terminal
23 stop mask warn SIGIO I/O is possible on a descriptor
24 stop mask warn SIGXCPU cpu time limit exceeded
25 stop mask warn SIGXFSZ file size limit exceeded
26 stop mask warn SIGVTALRM virtual time alarm
27 stop mask warn SIGPROF profiling timer alarm
28 stop mask warn SIGWINCH Window size change
29 stop mask warn SIGINFO status request from keyboard
30 stop mask warn SIGUSR1 User defined signal 1
31 stop mask warn SIGUSR2 User defined signal 2
;additional iphone exceptions
145 stop mask warn EXC_BAD_ACCESS Bad memory access
146 stop mask warn EXC_BAD_INSTRUCTION Bad instruction
147 stop mask warn EXC_ARITHMETIC Arithmetic exception
148 stop mask warn EXC_EMULATION Emulation exception
149 stop mask warn EXC_SOFTWARE Software exception
150 stop mask warn EXC_BREAKPOINT Breakpoint exception
; Bochs IA-32 emulator: raw exceptions
; (in addition to MS Windows exceptions defined above)
.bochs_win32
0x00 stop mask warn DIVIDE_BY_ZERO Divide by zero
0x01 stop mask warn SINGLE_STEP Single step
0x03 stop mask warn BREAKPOINT Breakpoint
0x04 stop mask warn INTO Interrupt on overflow
0x06 stop mask warn INVALID_OPCODE Invalid opcode
0x0C stop mask warn STACK_EXCEPTION Stack exception
0x0D stop mask warn GENERAL_PROTECTION_FAULT General protection fault
0x0E stop mask warn PAGE_FAULT Page fault at 0x%a, error code %a
0x10 stop mask warn FLOATING_POINT_ERROR Floating point error
.gdb
; gdb signals
1 stop mask warn SIGHUP Hangup
2 stop mask warn SIGINT Interrupt
3 stop mask warn SIGQUIT Quit
4 stop mask warn SIGILL Illegal instruction
5 stop mask warn SIGTRAP Trace/breakpoint trap
6 stop mask warn SIGABRT Aborted
7 stop mask warn SIGEMT Emulation trap
8 stop mask warn SIGFPE Arithmetic exception
9 stop mask warn SIGKILL Killed
10 stop mask warn SIGBUS Bus error
11 stop mask warn SIGSEGV Segmentation fault
12 stop mask warn SIGSYS Bad system call
13 stop mask warn SIGPIPE Broken pipe
14 stop mask warn SIGALRM Alarm clock
15 stop mask warn SIGTERM Terminated
16 stop mask warn SIGURG Urgent I/O condition
17 stop mask warn SIGSTOP Stopped (signal)
18 stop mask warn SIGTSTP Stopped (user)
19 stop mask warn SIGCONT Continued
20 stop mask warn SIGCHLD Child status changed
21 stop mask warn SIGTTIN Stopped (tty input)
22 stop mask warn SIGTTOU Stopped (tty output)
23 stop mask warn SIGIO I/O possible
24 stop mask warn SIGXCPU CPU time limit exceeded
25 stop mask warn SIGXFSZ File size limit exceeded
26 stop mask warn SIGVTALRM Virtual timer expired
27 stop mask warn SIGPROF Profiling timer expired
28 stop mask warn SIGWINCH Window size changed
29 stop mask warn SIGLOST Resource lost
30 stop mask warn SIGUSR1 User defined signal 1
31 stop mask warn SIGUSR2 User defined signal 2
32 stop mask warn SIGPWR Power fail/restart
33 stop mask warn SIGPOLL Pollable event occurred
34 stop mask warn SIGWIND SIGWIND
35 stop mask warn SIGPHONE SIGPHONE
36 stop mask warn SIGWAITING Process's LWPs are blocked
37 stop mask warn SIGLWP Signal LWP
38 stop mask warn SIGDANGER Swap space dangerously low
39 stop mask warn SIGGRANT Monitor mode granted
40 stop mask warn SIGRETRACT Need to relinquish monitor mode
41 stop mask warn SIGMSG Monitor mode data available
42 stop mask warn SIGSOUND Sound completed
43 stop mask warn SIGSAK Secure attention
44 stop mask warn SIGPRIO SIGPRIO
45 stop mask warn SIG33 Real-time event 33
46 stop mask warn SIG34 Real-time event 34
47 stop mask warn SIG35 Real-time event 35
48 stop mask warn SIG36 Real-time event 36
49 stop mask warn SIG37 Real-time event 37
50 stop mask warn SIG38 Real-time event 38
51 stop mask warn SIG39 Real-time event 39
52 stop mask warn SIG40 Real-time event 40
53 stop mask warn SIG41 Real-time event 41
54 stop mask warn SIG42 Real-time event 42
55 stop mask warn SIG43 Real-time event 43
56 stop mask warn SIG44 Real-time event 44
57 stop mask warn SIG45 Real-time event 45
58 stop mask warn SIG46 Real-time event 46
59 stop mask warn SIG47 Real-time event 47
60 stop mask warn SIG48 Real-time event 48
61 stop mask warn SIG49 Real-time event 49
62 stop mask warn SIG50 Real-time event 50
63 stop mask warn SIG51 Real-time event 51
64 stop mask warn SIG52 Real-time event 52
65 stop mask warn SIG53 Real-time event 53
66 stop mask warn SIG54 Real-time event 54
67 stop mask warn SIG55 Real-time event 55
68 stop mask warn SIG56 Real-time event 56
69 stop mask warn SIG57 Real-time event 57
70 stop mask warn SIG58 Real-time event 58
71 stop mask warn SIG59 Real-time event 59
72 stop mask warn SIG60 Real-time event 60
73 stop mask warn SIG61 Real-time event 61
74 stop mask warn SIG62 Real-time event 62
75 stop mask warn SIG63 Real-time event 63
76 stop mask warn SIGCANCEL LWP internal signal
77 stop mask warn SIG32 Real-time event 32
78 stop mask warn SIG64 Real-time event 64
79 stop mask warn SIG65 Real-time event 65
80 stop mask warn SIG66 Real-time event 66
81 stop mask warn SIG67 Real-time event 67
82 stop mask warn SIG68 Real-time event 68
83 stop mask warn SIG69 Real-time event 69
84 stop mask warn SIG70 Real-time event 70
85 stop mask warn SIG71 Real-time event 71
86 stop mask warn SIG72 Real-time event 72
87 stop mask warn SIG73 Real-time event 73
88 stop mask warn SIG74 Real-time event 74
89 stop mask warn SIG75 Real-time event 75
90 stop mask warn SIG76 Real-time event 76
91 stop mask warn SIG77 Real-time event 77
92 stop mask warn SIG78 Real-time event 78
93 stop mask warn SIG79 Real-time event 79
94 stop mask warn SIG80 Real-time event 80
95 stop mask warn SIG81 Real-time event 81
96 stop mask warn SIG82 Real-time event 82
97 stop mask warn SIG83 Real-time event 83
98 stop mask warn SIG84 Real-time event 84
99 stop mask warn SIG85 Real-time event 85
100 stop mask warn SIG86 Real-time event 86
101 stop mask warn SIG87 Real-time event 87
102 stop mask warn SIG88 Real-time event 88
103 stop mask warn SIG89 Real-time event 89
104 stop mask warn SIG90 Real-time event 90
105 stop mask warn SIG91 Real-time event 91
106 stop mask warn SIG92 Real-time event 92
107 stop mask warn SIG93 Real-time event 93
108 stop mask warn SIG94 Real-time event 94
109 stop mask warn SIG95 Real-time event 95
110 stop mask warn SIG96 Real-time event 96
111 stop mask warn SIG97 Real-time event 97
112 stop mask warn SIG98 Real-time event 98
113 stop mask warn SIG99 Real-time event 99
114 stop mask warn SIG100 Real-time event 100
115 stop mask warn SIG101 Real-time event 101
116 stop mask warn SIG102 Real-time event 102
117 stop mask warn SIG103 Real-time event 103
118 stop mask warn SIG104 Real-time event 104
119 stop mask warn SIG105 Real-time event 105
120 stop mask warn SIG106 Real-time event 106
121 stop mask warn SIG107 Real-time event 107
122 stop mask warn SIG108 Real-time event 108
123 stop mask warn SIG109 Real-time event 109
124 stop mask warn SIG110 Real-time event 110
125 stop mask warn SIG111 Real-time event 111
126 stop mask warn SIG112 Real-time event 112
127 stop mask warn SIG113 Real-time event 113
128 stop mask warn SIG114 Real-time event 114
129 stop mask warn SIG115 Real-time event 115
130 stop mask warn SIG116 Real-time event 116
131 stop mask warn SIG117 Real-time event 117
132 stop mask warn SIG118 Real-time event 118
133 stop mask warn SIG119 Real-time event 119
134 stop mask warn SIG120 Real-time event 120
135 stop mask warn SIG121 Real-time event 121
136 stop mask warn SIG122 Real-time event 122
137 stop mask warn SIG123 Real-time event 123
138 stop mask warn SIG124 Real-time event 124
139 stop mask warn SIG125 Real-time event 125
140 stop mask warn SIG126 Real-time event 126
141 stop mask warn SIG127 Real-time event 127
142 stop mask warn SIGINFO Information request
145 stop mask warn EXC_BAD_ACCESS Could not access memory
146 stop mask warn EXC_BAD_INSTRUCTION Illegal instruction/operand
147 stop mask warn EXC_ARITHMETIC Arithmetic exception
148 stop mask warn EXC_EMULATION Emulation instruction
149 stop mask warn EXC_SOFTWARE Software generated exception
150 stop mask warn EXC_BREAKPOINT Breakpoint
151 stop mask warn SIGLIBRT librt internal signal
.dalvik
1 nostop mask warn java.lang.Throwable The superclass of all errors and exceptions in the Java language

View File

@@ -0,0 +1,88 @@
/*
Android specific functions.
*/
// r_brk():
// B0003050 70 47 BX LR
static const uchar bxlr_thumb[] = { 0x70, 0x47 };
#define LINKER "/system/bin/linker"
//--------------------------------------------------------------------------
// on android /system/bin/linker comes without any symbols.
// Since there is no other way, we scan the data segment for
// dcd 1, ?, r_brk, 0, 0
// r_debug is located very close to the beginning of the data segment,
// we should find it fine. In any case, we check only the first 4KB.
bool linux_debmod_t::add_android_shlib_bpt(const meminfo_vec_t &miv, bool attaching)
{
// find read/writable linker memory range
meminfo_vec_t::const_iterator p;
ea_t linker_base = BADADDR;
for ( p=miv.begin(); p != miv.end(); ++p )
{
if ( p->name == LINKER )
{
if ( linker_base == BADADDR )
linker_base = p->start_ea;
// assume the data segment to be readable and writable
if ( (p->perm & 6) == 6 )
break;
}
}
if ( p == miv.end() )
{
msg("Failed to find data segment of " LINKER "\n");
return false;
}
// read max 2KB
uint32 buf[2048];
int nbytes = qmin(p->size(), sizeof(buf));
ea_t dataseg = p->start_ea;
nbytes = dbg_read_memory(dataseg, buf, nbytes, NULL);
uint32 *ptr = buf;
for ( int i=0; i < nbytes/4-5; i++, ptr++ )
{
if ( ptr[0] == 1 // version
&& (attaching || ptr[1] == 0) // r_map, 0 at the beginning
&& (ptr[2] & 1) != 0 && ptr[2] < dataseg // r_brk (Thumb pointer)
&& (attaching || ptr[3] == 0) // r_state: RT_CONSISTENT
&& ptr[4] == 0 ) // linker baseaddr: always zero?
{
ea_t r_brk = ptr[2] & ~1;
// check if linker is not relocated yet
if ( r_brk < linker_base )
r_brk += linker_base; // adjust address
uchar opcode[2];
if ( dbg_read_memory(r_brk, opcode, 2, NULL) == 2
&& memcmp(opcode, bxlr_thumb, 2) == 0 )
{
// found it!
if ( add_internal_bp(shlib_bpt, r_brk+1) )
{
dmsg("found r_debug (r_brk=%a)\n", r_brk);
return true;
}
}
}
}
msg("Failed to find r_debug in " LINKER "\n");
return false;
}
//--------------------------------------------------------------------------
// Android reports shared objects without any path. Try to find full path.
void linux_debmod_t::make_android_abspath(qstring *in_out_path)
{
if ( qisabspath(in_out_path->c_str()) )
return;
// Apparently /proc didn't return an absolute path. Check /system/lib.
// Normally we should not arrive here, this is just for safety.
char path[QMAXPATH];
qmakepath(path, sizeof(path), "/system/lib", in_out_path->c_str(), NULL);
if ( qfileexist(path) )
*in_out_path = path;
}

View File

@@ -0,0 +1,63 @@
/*
Android specific definitions
*/
#ifndef __ANDROID_HPP
#define __ANDROID_HPP
// Android NDK lacks link.h, so we have to use our own definitions
#include <android/api-level.h>
#include "dbg_rpc_handler.h"
struct link_map
{
uintptr_t l_addr;
char * l_name;
uintptr_t l_ld;
struct link_map * l_next;
struct link_map * l_prev;
};
struct r_debug
{
int32_t r_version;
struct link_map * r_map;
int32_t r_brk;
// Values for r_state
enum
{
RT_CONSISTENT,
RT_ADD,
RT_DELETE
};
int32_t r_state;
uintptr_t r_ldbase;
};
// pread64 is missing
#define pread64 pread
// thread_db.h lacks many definitions as well:
#define TD_MIN_EVENT_NUM 0
#define TD_MAX_EVENT_NUM 16
#define td_eventismember(set, n) (((set)->events & (1 << (n))) != 0)
typedef void *prgregset_t;
typedef void *prfpregset_t;
typedef int td_thr_type_e;
extern "C"
{
inline td_err_e td_init(void) { return TD_OK; }
inline td_err_e td_thr_set_event(const td_thrhandle_t *, td_thr_events_t *) { return TD_OK; }
inline td_err_e td_thr_setsigpending(const td_thrhandle_t *, unsigned char, const sigset_t *) { return TD_OK; }
}
// Other missing definitions:
typedef int32 __ptrace_request;
#define user_regs pt_regs
#endif // define __ANDROID_HPP

View File

@@ -0,0 +1,35 @@
#define REMOTE_DEBUGGER
#define RPC_CLIENT
static const char wanted_name[] = "Remote ARM Linux/Android debugger";
#define DEBUGGER_NAME "armlinux"
#define PROCESSOR_NAME "arm"
#define DEFAULT_PLATFORM_NAME "linux"
#define TARGET_PROCESSOR PLFM_ARM
#define DEBUGGER_ID DEBUGGER_ID_ARM_LINUX_USER
#define DEBUGGER_FLAGS (DBG_FLAG_REMOTE \
| DBG_FLAG_SMALLBLKS \
| DBG_FLAG_LOWCNDS \
| DBG_FLAG_DEBTHREAD \
| DBG_FLAG_PREFER_SWBPTS)
#define HAVE_APPCALL
#define S_FILETYPE f_ELF
#include <pro.h>
#include <idp.hpp>
#include <idd.hpp>
#include <ua.hpp>
#include <range.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <network.hpp>
#include "dbg_rpc_client.h"
#include "rpc_debmod.h"
rpc_debmod_t g_dbgmod(DEFAULT_PLATFORM_NAME);
#include "common_stub_impl.cpp"
#include "arm_local_impl.cpp"
#include "linux_local_impl.cpp"
#include "common_local_impl.cpp"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,352 @@
#ifndef __LINUX_DEBUGGER_MODULE__
#define __LINUX_DEBUGGER_MODULE__
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
#include "linuxbase_debmod.h"
extern "C"
{
#include <thread_db.h>
}
#include <deque>
typedef int HANDLE;
typedef uint32 DWORD;
#define INVALID_HANDLE_VALUE (-1)
using std::pair;
using std::make_pair;
//--------------------------------------------------------------------------
typedef std::map<ea_t, qstring> ea2name_t;
typedef std::map<qstring, ea_t> name2ea_t;
#ifndef WCONTINUED
#define WCONTINUED 8
#endif
#ifndef __WALL
#define __WALL 0x40000000
#endif
//--------------------------------------------------------------------------
// image information
struct image_info_t
{
image_info_t() : base(BADADDR), size(0), dl_crc(0) {}
image_info_t(
ea_t _base,
asize_t _size,
const qstring &_fname,
const qstring &_soname)
: base(_base), size(_size), fname(_fname), soname(_soname), dl_crc(0) {}
ea_t base;
asize_t size; // image size, currently 0
qstring fname;
qstring soname;
ea2name_t names;
qstring buildid;
qstring debuglink;
uint32 dl_crc;
};
typedef std::map<ea_t, image_info_t> images_t; // key: image base address
//--------------------------------------------------------------------------
enum thstate_t
{
RUNNING, // running
STOPPED, // waiting to be resumed after qwait
DYING, // we got a notification that the thread is about to die
DEAD, // dead thread; ignore any signals from it
};
//--------------------------------------------------------------------------
// thread information
struct thread_info_t
{
thread_info_t(int t)
: tid(t), suspend_count(0), user_suspend(0), child_signum(0), single_step(false),
state(STOPPED), waiting_sigstop(false), got_pending_status(false), pending_status(0) {}
int tid;
int suspend_count;
int user_suspend;
int child_signum;
bool single_step;
thstate_t state;
bool waiting_sigstop;
bool got_pending_status;
int pending_status;
qstring name; // thread name
bool is_running(void) const
{
return state == RUNNING && !waiting_sigstop && !got_pending_status;
}
};
//--------------------------------------------------------------------------
typedef std::map<HANDLE, thread_info_t> threads_t; // (tid -> info)
//--------------------------------------------------------------------------
enum ps_err_e
{
PS_OK, /* Success. */
PS_ERR, /* Generic error. */
PS_BADPID, /* Bad process handle. */
PS_BADLID, /* Bad LWP id. */
PS_BADADDR, /* Bad address. */
PS_NOSYM, /* Symbol not found. */
PS_NOFREGS /* FPU register set not available. */
};
struct ps_prochandle
{
pid_t pid;
};
#ifndef UINT32_C
# define UINT32_C uint32
#endif
//--------------------------------------------------------------------------
struct internal_bpt
{
ea_t bpt_addr;
uchar saved[BPT_CODE_SIZE];
uchar nsaved;
internal_bpt(): bpt_addr(0), nsaved(0) {} //-V730 Not all members of a class are initialized
};
//--------------------------------------------------------------------------
struct mapfp_entry_t
{
ea_t ea1;
ea_t ea2;
ea_t offset;
uint64 inode;
char perm[8];
char device[8];
uint8 bitness; // Number of bits in segment addresses (0-16bit, 1-32bit, 2-64bit)
qstring fname;
bool empty(void) const { return ea1 >= ea2; }
};
//--------------------------------------------------------------------------
struct chk_signal_info_t
{
pid_t pid;
int status;
int timeout_ms;
chk_signal_info_t(int _timeout_ms)
{
timeout_ms = _timeout_ms;
pid = 0;
status = 0;
}
};
//--------------------------------------------------------------------------
enum attach_mode_t
{
AMT_NO_ATTACH,
AMT_ATTACH_NORMAL,
AMT_ATTACH_BROKEN
};
//--------------------------------------------------------------------------
class linux_debmod_t: public linuxbase_debmod_t
{
typedef linuxbase_debmod_t inherited;
// thread_db related data and functions:
struct ps_prochandle prochandle;
td_thragent_t *ta;
internal_bpt birth_bpt; // thread created
internal_bpt death_bpt; // thread exited
internal_bpt shlib_bpt; // shared lib list changed
bool complained_shlib_bpt;
void make_android_abspath(qstring *in_out_path);
bool add_android_shlib_bpt(const meminfo_vec_t &miv, bool attaching);
bool add_internal_bp(internal_bpt &bp, ea_t addr);
bool erase_internal_bp(internal_bpt &bp);
bool tdb_enable_event(td_event_e event, internal_bpt *bp);
void tdb_update_threads(void);
bool tdb_new(void);
void tdb_delete(void);
void tdb_handle_messages(int pid);
void dead_thread(int tid, thstate_t state);
void store_pending_signal(int pid, int status);
bool activate_multithreading();
// procfs
void procfs_collect_threads(void);
bool attach_collected_thread(unsigned long lwp);
bool get_thread_name(qstring *thr_name, thid_t tid);
void update_thread_names(thread_name_vec_t *thr_names);
// list of debug names not yet sent to IDA
name_info_t pending_names;
name_info_t nptl_names;
pid_t check_for_signal(int *status, int pid, int timeout_ms) const;
int find_largest_addrsize(const meminfo_vec_t &miv);
void _import_dll(image_info_t &ii);
void _import_symbols_from_file(name_info_t *out, image_info_t &ii);
public:
easet_t dlls_to_import; // list of dlls to import information from
images_t dlls; // list of loaded DLLs
threads_t threads;
qvector<thid_t> deleted_threads;
qvector<thid_t> seen_threads; // thread was born and appeared too early
// debugged process information
HANDLE process_handle;
HANDLE thread_handle;
bool threads_collected = false;
bool exited; // Did the process exit?
easet_t removed_bpts; // removed breakpoints
FILE *mapfp; // map file handle
int npending_signals; // number of pending signals
bool may_run;
bool requested_to_suspend;
bool in_event; // IDA kernel is handling a debugger event
qstring interp;
qstring exe_path; // name of the executable file
ea_t nptl_base; // base of 'libpthread.so'
regctx_t *reg_ctx;
linux_debmod_t();
~linux_debmod_t();
void init_reg_ctx(void);
void term_reg_ctx(void);
thread_info_t &add_thread(int tid);
void del_thread(int tid);
thread_info_t *get_thread(thid_t tid);
bool retrieve_pending_signal(pid_t *pid, int *status);
int get_debug_event(debug_event_t *event, int timeout_ms);
bool del_pending_event(event_id_t id, const char *module_name);
void enqueue_event(const debug_event_t &ev, queue_pos_t pos);
bool suspend_all_threads(void);
bool resume_all_threads(void);
int dbg_freeze_threads(thid_t tid, bool exclude=true);
int dbg_thaw_threads(thid_t tid, bool exclude=true);
void set_thread_state(thread_info_t &ti, thstate_t state) const;
bool resume_app(thid_t tid);
bool has_pending_events(void);
bool read_asciiz(tid_t tid, ea_t ea, char *buf, size_t bufsize, bool suspend=false);
int _read_memory(int tid, ea_t ea, void *buffer, int size, bool suspend=false);
int _write_memory(int tid, ea_t ea, const void *buffer, int size, bool suspend=false);
void add_dll(ea_t base, asize_t size, const char *modname, const char *soname);
asize_t calc_module_size(const meminfo_vec_t &miv, const memory_info_t *mi) const;
void enum_names(const char *libpath=NULL);
bool add_shlib_bpt(const meminfo_vec_t &miv, bool attaching);
bool gen_library_events(int tid);
bool emulate_retn(int tid);
void cleanup(void);
bool handle_process_start(pid_t pid, attach_mode_t attaching);
drc_t get_memory_info(meminfo_vec_t &areas, bool suspend);
bool set_hwbpts(HANDLE hThread) const;
virtual bool refresh_hwbpts() override;
void handle_dll_movements(const meminfo_vec_t &miv);
bool idaapi thread_get_fs_base(thid_t tid, int reg_idx, ea_t *pea) const;
bool read_mapping(mapfp_entry_t *me);
bool get_soname(const char *fname, qstring *soname) const;
ea_t find_pending_name(const char *name);
bool handle_hwbpt(debug_event_t *event);
bool thread_is_known(const td_thrinfo_t &info) const;
bool listen_thread_events(const td_thrinfo_t &info, const td_thrhandle_t *th_p);
void attach_to_thread(const td_thrinfo_t &info);
bool attach_to_thread(int tid, ea_t ea);
void handle_extended_wait(bool *handled, const chk_signal_info_t &csi);
bool finish_attaching(int tid, ea_t ea, bool use_ip);
bool check_for_new_events(chk_signal_info_t *csi, bool *event_prepared);
//
virtual void idaapi dbg_set_debugging(bool _debug_debugger) override;
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) override;
virtual void idaapi dbg_term(void) override;
virtual drc_t idaapi dbg_detach_process(void) override;
virtual drc_t idaapi dbg_start_process(
const char *path,
const char *args,
const char *startdir,
int flags,
const char *input_path,
uint32 input_file_crc32,
qstring *errbuf) override;
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_ms) override;
virtual drc_t idaapi dbg_attach_process(pid_t process_id, int event_id, int flags, qstring *errbuf) override;
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) override;
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) override;
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) override;
virtual void idaapi dbg_stopped_at_debug_event(import_infos_t *infos, bool dlls_added, thread_name_vec_t *thr_names) override;
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) override;
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) override;
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) override;
virtual drc_t idaapi dbg_read_registers(
thid_t thread_id,
int clsmask,
regval_t *values,
qstring *errbuf) override;
virtual drc_t idaapi dbg_write_register(
thid_t thread_id,
int reg_idx,
const regval_t *value,
qstring *errbuf) override;
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) override;
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &areas, qstring *errbuf) override;
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) override;
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) override;
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) override;
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) override;
virtual int idaapi handle_ioctl(int fn, const void *buf, size_t size, void **outbuf, ssize_t *outsize) override;
virtual bool idaapi write_registers(
thid_t tid,
int start,
int count,
const regval_t *values) override;
virtual int dbg_freeze_threads_except(thid_t tid) override { return dbg_freeze_threads(tid); }
virtual int dbg_thaw_threads_except(thid_t tid) override { return dbg_thaw_threads(tid); }
virtual bool idaapi dbg_continue_broken_connection(pid_t pid) override;
virtual bool idaapi dbg_prepare_broken_connection(void) override;
// thread_db
void display_thrinfo(thid_t tid);
void display_all_threads();
void cleanup_breakpoints(void);
void cleanup_signals(void);
bool fix_instruction_pointer(void) const;
#ifdef __ARM__
virtual void adjust_swbpt(ea_t *p_ea, int *p_len) override;
#endif
#ifdef LDEB
void log(thid_t tid, const char *format, ...);
#endif
};
#endif

View File

@@ -0,0 +1,15 @@
{
global:
PLUGIN;
ps_pglobal_lookup;
ps_pdread;
ps_pdwrite;
ps_lgetregs;
ps_lsetregs;
ps_lgetfpregs;
ps_lsetfpregs;
ps_getpid;
ps_get_thread_area;
local:
*;
};

View File

@@ -0,0 +1,66 @@
#include <loader.hpp>
//--------------------------------------------------------------------------
// installs or uninstalls debugger specific idc functions
inline bool register_idc_funcs(bool)
{
return true;
}
//--------------------------------------------------------------------------
void idaapi rebase_if_required_to(ea_t new_base)
{
ea_t base = get_imagebase();
if ( base != BADADDR && new_base != BADADDR && base != new_base )
rebase_or_warn(base, new_base);
}
//--------------------------------------------------------------------------
static bool init_plugin(void)
{
#ifndef RPC_CLIENT
if ( !init_subsystem() )
return false;
#endif
bool ok = false;
do
{
if ( !netnode::inited() || is_miniidb() || inf_is_snapshot() )
{
#ifdef __LINUX__
// local debugger is available if we are running under Linux
return true;
#else
// for other systems only the remote debugger is available
if ( debugger.is_remote() )
return true;
break; // failed
#endif
}
if ( inf_get_filetype() != f_ELF )
break;
processor_t &ph = PH;
if ( ph.id != TARGET_PROCESSOR && ph.id != -1 )
break;
ok = true;
} while ( false );
#ifndef RPC_CLIENT
if ( !ok )
term_subsystem();
#endif
return ok;
}
//--------------------------------------------------------------------------
inline void term_plugin(void)
{
#ifndef RPC_CLIENT
term_subsystem();
#endif
}
//--------------------------------------------------------------------------
static const char comment[] = "Userland linux debugger plugin.";

View File

@@ -0,0 +1,34 @@
#define REMOTE_DEBUGGER
#define RPC_CLIENT
static const char wanted_name[] = "Remote Linux debugger";
#define DEBUGGER_NAME "linux"
#define PROCESSOR_NAME "metapc"
#define DEFAULT_PLATFORM_NAME "linux"
#define TARGET_PROCESSOR PLFM_386
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_LINUX_USER
#define DEBUGGER_FLAGS (DBG_FLAG_REMOTE \
| DBG_FLAG_LOWCNDS \
| DBG_FLAG_DEBTHREAD)
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
#define HAVE_APPCALL
#define S_FILETYPE f_ELF
#include <pro.h>
#include <idp.hpp>
#include <idd.hpp>
#include <ua.hpp>
#include <range.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <network.hpp>
#include "dbg_rpc_client.h"
#include "rpc_debmod.h"
rpc_debmod_t g_dbgmod(DEFAULT_PLATFORM_NAME);
#include "common_stub_impl.cpp"
#include "pc_local_impl.cpp"
#include "linux_local_impl.cpp"
#include "common_local_impl.cpp"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
static const char wanted_name[] = "Local Linux debugger";
#define DEBUGGER_NAME "linux"
#define PROCESSOR_NAME "metapc"
#define TARGET_PROCESSOR PLFM_386
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_LINUX_USER
#define DEBUGGER_FLAGS (DBG_FLAG_LOWCNDS \
| DBG_FLAG_DEBTHREAD)
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
#define HAVE_APPCALL
#define S_FILETYPE f_ELF
#include <fpro.h>
#include <idd.hpp>
#include <ua.hpp>
#include <range.hpp>
#include <loader.hpp>
#include "linux_debmod.h"
linux_debmod_t g_dbgmod;
bool ignore_sigint = false;
#include "common_stub_impl.cpp"
#include "pc_local_impl.cpp"
#include "linux_local_impl.cpp"
#include "common_local_impl.cpp"

View File

@@ -0,0 +1,8 @@
#include <pro.h>
#include "linux_debmod.h"
//--------------------------------------------------------------------------
pid_t linux_debmod_t::check_for_signal(int *status, int _pid, int timeout_ms) const
{
return qwait_timed(status, _pid, __WALL | WCONTINUED, timeout_ms);
}

View File

@@ -0,0 +1,146 @@
#include <fpro.h>
#include <prodir.h>
#include <diskio.hpp>
#include "linuxbase_debmod.h"
//--------------------------------------------------------------------------
static inline const char *str_bitness(int bitness)
{
switch ( bitness )
{
case 8:
return "[64]";
case 4:
return "[32]";
default:
return "[x]";
}
}
//--------------------------------------------------------------------------
static void build_process_ext_name(ext_process_info_t *pinfo)
{
pinfo->ext_name = str_bitness(pinfo->addrsize);
char buf[QMAXPATH];
qsnprintf(buf, sizeof(buf), "/proc/%u/cmdline", pinfo->pid);
FILE *cmdfp = qfopen(buf, "r");
if ( cmdfp == nullptr )
return;
int size = qfread(cmdfp, buf, sizeof(buf));
buf[size] = '\0';
qfclose(cmdfp);
#ifdef __ANDROID__
while ( size >= 0 && buf[size] == '\0' )
size--;
size++;
#endif
// arguments are separated by '\0'
for ( int i=0; i < size; )
{
const char *in = &buf[i];
qstring arg = in;
quote_cmdline_arg(&arg);
pinfo->ext_name.append(" ");
pinfo->ext_name.append(arg);
i += strlen(in) + 1;
}
}
//--------------------------------------------------------------------------
// Returns the file name assciated with pid
bool idaapi linuxbase_debmod_t::get_exec_fname(
int _pid,
char *buf,
size_t bufsize)
{
char path[QMAXPATH];
qsnprintf(path, sizeof(path), "/proc/%u/exe", _pid);
int len = readlink(path, buf, bufsize-1);
if ( len > 0 )
{
buf[len] = '\0';
return true;
}
else
{
// ESXi keeps the real file name inside /proc/PID/exe (which is not a link)
FILE *fp = qfopen(path, "r");
if ( fp != NULL )
{
len = qfread(fp, buf, bufsize);
qfclose(fp);
if ( len > 1 && len < bufsize && buf[0] == '/' ) // sanity check
{
buf[len] = '\0';
return true;
}
}
buf[0] = '\0';
return false;
}
}
//--------------------------------------------------------------------------
// Get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
int idaapi linuxbase_debmod_t::get_process_bitness(int _pid)
{
char fname[QMAXPATH];
qsnprintf(fname, sizeof(fname), "/proc/%u/maps", _pid);
FILE *mapfp = fopenRT(fname);
if ( mapfp == NULL )
return 0;
int bitness = 4;
qstring line;
while ( qgetline(&line, mapfp) >= 0 )
{
if ( line.empty() )
continue;
ea_t ea1;
ea_t ea2;
if ( qsscanf(line.begin(), "%a-%a ", &ea1, &ea2) == 2 )
{
size_t pos = line.find('-');
if ( pos != qstring::npos && pos > 8 )
{
bitness = 8;
break;
}
}
}
qfclose(mapfp);
return bitness;
}
//--------------------------------------------------------------------------
int idaapi linuxbase_debmod_t::get_process_list(procvec_t *list, qstring *)
{
int mypid = getpid();
list->clear();
qffblk64_t fb;
for ( int code = qfindfirst("/proc/*", &fb, FA_DIREC);
code == 0;
code = qfindnext(&fb) )
{
if ( !qisdigit(fb.ff_name[0]) )
continue;
ext_process_info_t pinfo;
pinfo.pid = atoi(fb.ff_name);
if ( pinfo.pid == mypid )
continue;
char buf[MAXSTR];
if ( !get_exec_fname(pinfo.pid, buf, sizeof(buf)) )
continue; // we skip the process because we cannot debug it anyway
pinfo.name = buf;
pinfo.addrsize = get_process_bitness(pinfo.pid);
build_process_ext_name(&pinfo);
list->push_back(pinfo);
}
return list->size();
}

View File

@@ -0,0 +1,30 @@
#ifndef __LINUXBASE_HPP__
#define __LINUXBASE_HPP__
#include "debmod.h"
// Base class for linux modules
#ifdef __ARM__
# define BASE_DEBUGGER_MODULE arm_debmod_t
# include "arm_debmod.h"
# define BPT_CODE_SIZE ARM_BPT_SIZE
#else
# define BASE_DEBUGGER_MODULE pc_debmod_t
# include "pc_debmod.h"
# define BPT_CODE_SIZE X86_BPT_SIZE
#endif
class linuxbase_debmod_t: public BASE_DEBUGGER_MODULE
{
typedef BASE_DEBUGGER_MODULE inherited;
protected:
// return number of processes, -1 - not implemented
virtual int idaapi get_process_list(procvec_t *proclist, qstring *errbuf) override;
// return the file name assciated with pid
virtual bool idaapi get_exec_fname(int pid, char *buf, size_t bufsize) newapi;
// get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
virtual int idaapi get_process_bitness(int pid) newapi;
};
#endif

164
idasdk76/dbg/linux/makefile Normal file
View File

@@ -0,0 +1,164 @@
include ../../allmake.mak
GOALS-$(BUILD_IDA) += modules # target in $(IDA)module.mak
GOALS-$(BUILD_DBGSRV) += server # target in $(IDA)dbg/server.mak
.PHONY: $(GOALS-1)
all: $(GOALS-1)
#----------------------------------------------------------------------
ifdef __LINUX__
SERVER = linux_server$(SUFF64)
endif
ifdef SERVER
SERVERS += $(call server_exe,$(SERVER))
endif
#----------------------------------------------------------------------
USER = $(call module_dll,linux_user)
ifeq ($(and $(BUILD_IDA),$(__LINUX__)),1)
MODULES += $(USER)
endif
#----------------------------------------------------------------------
# we explicitly added our module targets
NO_DEFAULT_TARGETS = 1
# NOTE: all MODULES must be defined before including plugin.mak.
include ../plugin.mak
# NOTE: target-specific rules and dependencies that use variable
# expansion to name the target (such as "$(MODULE): [...]") must
# come after including plugin.mak
#----------------------------------------------------------------------
# select OBJS common to user plugin and debugger server
ifeq ($(or $(__LINUX__),$(__ANDROID__),$(__ANDROID_X86__),$(__ARMLINUX__)),1)
BUILD_LINUX:=1
endif
BASE_OBJS-$(BUILD_LINUX) += $(F)linuxbase_debmod$(O)
BASE_OBJS-$(BUILD_LINUX) += $(F)linux_debmod$(O)
BASE_OBJS-$(BUILD_LINUX) += $(F)linux_wait$(O)
BASE_OBJS += $(BASE_OBJS-1) $(F)symelf$(O)
#----------------------------------------------------------------------
ifdef __LINUX__
SERVER_LDFLAGS += -Wl,--version-script=linux_debmod.script
SERVER_LDFLAGS += $(OUTMAP)$(F)$(@F).map
SERVER_STDLIBS += -lthread_db -lrt -lc -lpthread -ldl
endif
SERVER_OBJS += $(BASE_OBJS)
# suppress warnings for libthread_db.c
$(F)libthread_db$(O): WARNS = $(NOWARNS)
include ../server.mak
#----------------------------------------------------------------------
STUB_OBJS += $(F)linux_stub$(O)
$(STUB): MODULE_OBJS += $(STUB_OBJS)
$(STUB): $(STUB_OBJS)
#----------------------------------------------------------------------
USER_OBJS += $(F)linux_user$(O)
USER_OBJS += $(BASE_OBJS)
$(USER): MODULE_OBJS += $(USER_OBJS)
$(USER): $(USER_OBJS)
$(USER): DEFFILE = linux_debmod.script
$(USER): STDLIBS += -ldl
$(USER): STDLIBS += -lthread_db
ifeq ($(COMPILER_NAME),gcc)
$(USER): LDFLAGS += -Wl,--export-dynamic
endif
#----------------------------------------------------------------------
include $(IDA)objdir.mak
# MAKEDEP dependency list ------------------
$(F)armlinux_stub$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../arm_local_impl.cpp ../arm_regs.cpp ../arm_regs.hpp \
../common_local_impl.cpp ../common_stub_impl.cpp \
../dbg_rpc_client.h ../dbg_rpc_engine.h ../deb_arm.hpp \
../debmod.h ../rpc_debmod.h armlinux_stub.cpp \
linux_local_impl.cpp
$(F)libthread_db$(O): libthread_db.c
$(F)linux_debmod$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)diskio.hpp $(I)err.h $(I)fpro.h $(I)funcs.hpp \
$(I)ida.hpp $(I)idd.hpp $(I)idp.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)name.hpp $(I)netnode.hpp $(I)network.hpp $(I)pro.h \
$(I)prodir.h $(I)range.hpp $(I)segment.hpp $(I)ua.hpp \
$(I)xref.hpp ../../plugins/dwarf/look_for_debug_file.cpp \
../arm_debmod.h ../arm_regs.hpp ../dbg_rpc_engine.h \
../dbg_rpc_handler.h ../dbg_rpc_handler_ioctls.h \
../deb_arm.hpp ../deb_pc.hpp ../debmod.h ../pc_debmod.h \
../pc_regs.hpp android.cpp android.hpp linux_debmod.cpp \
linux_debmod.h linux_threads.cpp linuxbase_debmod.h \
symelf.hpp
$(F)linux_stub$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../common_local_impl.cpp ../common_stub_impl.cpp \
../dbg_rpc_client.h ../dbg_rpc_engine.h ../deb_pc.hpp \
../debmod.h ../pc_local_impl.cpp ../pc_regs.hpp \
../rpc_debmod.h linux_local_impl.cpp linux_stub.cpp
$(F)linux_user$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../arm_debmod.h ../common_local_impl.cpp \
../common_stub_impl.cpp ../deb_arm.hpp ../deb_pc.hpp \
../debmod.h ../pc_debmod.h ../pc_local_impl.cpp \
../pc_regs.hpp linux_debmod.h linux_local_impl.cpp \
linux_user.cpp linuxbase_debmod.h
$(F)linux_wait$(O): $(I)bytes.hpp $(I)ida.hpp $(I)idd.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)ua.hpp \
$(I)xref.hpp ../arm_debmod.h ../deb_arm.hpp \
../deb_pc.hpp ../debmod.h ../pc_debmod.h ../pc_regs.hpp \
linux_debmod.h linux_wait.cpp linuxbase_debmod.h
$(F)linuxbase_debmod$(O): $(I)bytes.hpp $(I)diskio.hpp $(I)fpro.h \
$(I)ida.hpp $(I)idd.hpp $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)nalt.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)prodir.h $(I)range.hpp \
$(I)ua.hpp $(I)xref.hpp ../arm_debmod.h ../deb_arm.hpp \
../deb_pc.hpp ../debmod.h ../pc_debmod.h ../pc_regs.hpp \
linuxbase_debmod.cpp linuxbase_debmod.h
$(F)symelf$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)diskio.hpp $(I)entry.hpp \
$(I)fixup.hpp $(I)fpro.h $(I)funcs.hpp \
$(I)ida.hpp $(I)idd.hpp $(I)idp.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
$(I)name.hpp $(I)netnode.hpp $(I)network.hpp \
$(I)offset.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/elf/../idaldr.h ../../ldr/elf/common.cpp \
../../ldr/elf/elf.h ../../ldr/elf/elfbase.h \
\
\
\
\
\
\
\
\
\
\
\
\
../../ldr/elf/reader.cpp \
../debmod.h symelf.cpp symelf.hpp

View File

@@ -0,0 +1,239 @@
// read elf symbols
#include <fpro.h>
#include <kernwin.hpp>
#include <diskio.hpp>
#include "../../ldr/elf/elfbase.h"
#include "../../ldr/elf/elf.h"
#include "debmod.h"
#include "symelf.hpp"
#include "../../ldr/elf/common.cpp"
#include "../../ldr/elf/reader.cpp"
inline uint32 low(uint32 x) { return x; }
uval_t imagebase;
//--------------------------------------------------------------------------
//lint -e{1764} could be declared const ref
static int handle_symbol(
reader_t &reader,
int shndx,
int _info,
uint32 st_name,
uval_t st_value,
slice_type_t slice_type,
symbol_visitor_t &sv)
{
if ( shndx == SHN_UNDEF
|| shndx == SHN_LOPROC
|| shndx == SHN_HIPROC
|| shndx == SHN_ABS )
{
return 0;
}
int type = ELF_ST_TYPE(_info);
if ( type != STT_OBJECT && type != STT_FUNC )
return 0;
if ( st_name == 0 )
return 0;
if ( imagebase != uval_t(-1) )
st_value -= imagebase;
qstring name;
reader.get_name(&name, slice_type, st_name);
return sv.visit_symbol(st_value, name.c_str());
}
//--------------------------------------------------------------------------
static int load_symbols(
reader_t &reader,
const elf_shdr_t &section,
slice_type_t slice_type,
symbol_visitor_t &sv)
{
int code = 0;
sym_rel *sym;
buffered_input_t<sym_rel> symbols_input(reader, section);
for ( elf_sym_idx_t i = 0; code == 0 && symbols_input.next(sym); ++i )
{
if ( i == 0 ) // skip _UNDEF
continue;
code = handle_symbol(reader,
sym->original.st_shndx,
sym->original.st_info,
sym->original.st_name,
sym->original.st_value,
slice_type,
sv);
}
return code;
}
//--------------------------------------------------------------------------
static bool map_pht(reader_t &reader)
{
if ( !reader.read_program_headers() )
return false;
imagebase = reader.pheaders.get_image_base();
return true;
}
//----------------------------------------------------------------------------
static bool silent_handler(const reader_t &reader, reader_t::errcode_t code, ...)
{
return reader.is_warning(code); // resume after warnings
}
//--------------------------------------------------------------------------
static int _load_elf_symbols(linput_t *li, symbol_visitor_t &sv)
{
reader_t reader(li);
reader.set_handler(silent_handler);
if ( !reader.read_ident() || !reader.read_header() )
return -1;
const elf_ident_t &ident = reader.get_ident();
uint8 elf_class = ident.elf_class;
if ( elf_class != ELFCLASS32 && elf_class != ELFCLASS64 )
return -1;
uint8 elf_data_ord = ident.bytesex;
if ( elf_data_ord != ELFDATA2LSB && elf_data_ord != ELFDATA2MSB )
return -1;
section_headers_t &sections = reader.sections;
dynamic_linking_tables_t dlt;
int code = 0;
elf_ehdr_t &header = reader.get_header();
if ( header.has_pht() && !map_pht(reader) )
return -1;
reader.read_section_headers();
// Try and acquire dynamic linking tables info.
dlt = reader.sections.get_dynamic_linking_tables_info();
if ( !dlt.is_valid() )
dlt = reader.pheaders.get_dynamic_linking_tables_info();
// Parse dynamic info if available
dynamic_info_t di;
if ( dlt.is_valid() )
{
reader_t::dyninfo_tags_t dyninfo_tags;
dyninfo_tags.reserve(10);
if ( reader.read_dynamic_info_tags(&dyninfo_tags, dlt)
&& reader.parse_dynamic_info(&di, dyninfo_tags)
&& (sv.velf & VISIT_DYNINFO) != 0 )
{
reader.set_di_strtab(reader.dyn_strtab, di.strtab());
typedef reader_t::dyninfo_tags_t::const_iterator const_it;
for ( const_it dyn = dyninfo_tags.begin();
dyn != dyninfo_tags.end();
++dyn )
{
qstring name;
switch ( dyn->d_tag )
{
case DT_SONAME:
case DT_RPATH:
case DT_RUNPATH:
case DT_NEEDED:
reader.get_name(&name, reader.dyn_strtab, uint32(dyn->d_un));
break;
}
if ( sv.visit_dyninfo(dyn->d_tag, name.c_str(), dyn->d_un) != 0 )
break;
};
}
}
if ( (sv.velf & VISIT_INTERP) != 0 )
{
elf_shdr_t *interp_sh = reader.sections.get_wks(WKS_INTERP);
if ( interp_sh != NULL )
{
qstring name;
reader.get_string_at(&name, interp_sh->sh_offset);
code = sv.visit_interp(name.c_str());
if ( code != 0 )
return code;
}
}
if ( (sv.velf & VISIT_SYMBOLS) != 0 )
{
elf_shndx_t symtab = sections.get_index(WKS_SYMTAB);
elf_shndx_t dynsym = sections.get_index(WKS_DYNSYM);
elf_shdr_t fake_section;
if ( symtab != 0 || dynsym != 0 )
{
// Loading symbols
if ( symtab != 0 )
code = load_symbols(reader, *sections.getn(symtab), SLT_SYMTAB, sv);
if ( code == 0 && dynsym != 0 )
code = load_symbols(reader, *sections.getn(dynsym), SLT_DYNSYM, sv);
}
else if ( di.fill_section_header(&fake_section, DIT_SYMTAB) )
{
code = load_symbols(reader, fake_section, SLT_DYNSYM, sv);
}
}
notes_t notes(&reader);
if ( (sv.velf & VISIT_BUILDID) != 0 && reader.read_notes(&notes) )
{
qstring id;
if ( notes.get_build_id(&id) )
{
code = sv.visit_buildid(id.c_str());
if ( code != 0 )
return code;
}
}
if ( (sv.velf & VISIT_DBGLINK ) != 0 )
{
uint32 crc;
qstring debuglink;
if ( sections.is_initialized()
&& sections.read_gnu_debuglink(&debuglink, &crc) )
{
code = sv.visit_debuglink(debuglink.c_str(), crc);
if ( code != 0 )
return code;
}
}
return code;
}
//--------------------------------------------------------------------------
static int load_linput_elf_symbols(linput_t *li, symbol_visitor_t &sv)
{
if ( li == NULL )
return -1;
int code;
// there is thread unsafe code in elf handling, so use locks
lock_begin();
{
code = _load_elf_symbols(li, sv);
}
lock_end();
close_linput(li);
return code;
}
//--------------------------------------------------------------------------
int load_elf_symbols(const char *fname, symbol_visitor_t &sv, bool remote)
{
return load_linput_elf_symbols(open_linput(fname, remote), sv);
}

View File

@@ -0,0 +1,30 @@
// read symbols from an elf file
#ifndef __SYMELF__
#define __SYMELF__
struct symbol_visitor_t
{
symbol_visitor_t(int visit_flags) : velf(visit_flags) {}
int velf;
#define VISIT_SYMBOLS 0x0001
#define VISIT_INTERP 0x0002
#define VISIT_DYNINFO 0x0004
#define VISIT_SEGMENTS 0x0008
#define VISIT_BUILDID 0x0010
#define VISIT_DBGLINK 0x0020
// any callback returns nonzero - stop enumeration
virtual int visit_symbol(ea_t /*ea*/, const char * /*name*/) { return 0; }
virtual int visit_interp(const char * /*name*/) { return 0; }
virtual int visit_segment(ea_t /*start*/, size_t /*size*/, const char * /*name*/) { return 0; }
virtual int visit_dyninfo(uint64 /*tag*/, const char * /*name*/, uint64 /*value*/) { return 0; }
virtual int visit_buildid(const char * /*Build ID*/) { return 0; }
virtual int visit_debuglink(const char * /*debug*/, uint32 /*crc*/) { return 0; }
};
// returns -1 on errors
// otherwise returns the non-zero code returned by the visitor or 0
int load_elf_symbols(const char *fname, symbol_visitor_t &sv, bool remote=false);
#endif

60
idasdk76/dbg/makefile Normal file
View File

@@ -0,0 +1,60 @@
include ../allmake.mak
# the following platforms only build the servers, and not the plugins.
ifneq ($(or $(__ANDROID__),$(__ANDROID_X86__),$(__ARMLINUX__),$(USE_STATIC_RUNTIME)),1)
DBG_PLUGIN = $(L)dbg_plugin$(A)
CONFIGS += exceptions.cfg
endif
BASE_LIBS += $(L)dbg_rpc$(A)
BASE_LIBS += $(L)dbg_server$(A)
BASE_LIBS += $(DBG_PLUGIN)
BASE_LIBS += $(L)dbg_proc$(A)
GOALS += $(addprefix $(C),$(CONFIGS))
ALLDIRS += linux
# ALLDIRS += mac
ALLDIRS += win32
# default target
all: $(ALLDIRS) $(GOALS)
# dependencies
$(ALLDIRS): $(BASE_LIBS)
# recipes for subdirs
.PHONY: $(ALLDIRS)
$(sort $(ALLDIRS)):
$(Q)$(MAKE) -C $@
clean::
$(foreach dir,$(ALLDIRS),$(MAKE) -C $(dir) clean;)
#----------------------------------------------------------------------
RPC_OBJS += $(F)dbg_rpc_engine$(O)
RPC_OBJS += $(F)dbg_rpc_hlp$(O)
$(L)dbg_rpc$(A): $(call lib, $(RPC_OBJS))
#----------------------------------------------------------------------
SERVER_OBJS += $(F)bin_search$(O)
SERVER_OBJS += $(F)dbg_rpc_handler$(O)
SERVER_OBJS += $(F)debmod$(O)
SERVER_OBJS += $(F)server$(O)
$(L)dbg_server$(A): $(call lib, $(SERVER_OBJS))
#----------------------------------------------------------------------
PLUGIN_OBJS += $(F)bin_search$(O)
PLUGIN_OBJS += $(F)dbg_rpc_client$(O)
PLUGIN_OBJS += $(F)debmod$(O)
PLUGIN_OBJS += $(F)rpc_debmod$(O)
$(L)dbg_plugin$(A): $(call lib, $(PLUGIN_OBJS))
#----------------------------------------------------------------------
PROC_OBJS += $(F)arm_debmod$(O)
PROC_OBJS += $(F)arm_regs$(O)
PROC_OBJS += $(F)pc_debmod$(O)
PROC_OBJS += $(F)pc_regs$(O)
$(L)dbg_proc$(A): $(call lib, $(PROC_OBJS))
include $(IDA)objdir.mak

304
idasdk76/dbg/pc_debmod.cpp Normal file
View File

@@ -0,0 +1,304 @@
#ifdef __NT__
#include <windows.h>
#endif
#include <pro.h>
#include <ua.hpp>
#include "pc_debmod.h"
//--------------------------------------------------------------------------
//lint -esym(1566,pc_debmod_t::hwbpt_ea,pc_debmod_t::hwbpt_type)
//lint -esym(1566,pc_debmod_t::dr6,pc_debmod_t::dr7)
pc_debmod_t::pc_debmod_t()
{
static const uchar bpt[] = X86_BPT_CODE;
bpt_code.append(bpt, sizeof(bpt));
sp_idx = R_ESP;
pc_idx = R_EIP;
sr_idx = R_EFLAGS;
fs_idx = R_FS;
gs_idx = R_GS;
cs_idx = R_CS;
ds_idx = R_DS;
es_idx = R_ES;
ss_idx = R_SS;
nregs = X86_NREGS;
cleanup_hwbpts();
set_platform(get_local_platform());
}
//--------------------------------------------------------------------------
int pc_debmod_t::get_regidx(const char *regname, int *clsmask)
{
return x86_get_regidx(clsmask, regname);
}
//--------------------------------------------------------------------------
int idaapi pc_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t ea, int /* len */)
{
if ( type == BPT_SOFT )
return BPT_OK;
return find_hwbpt_slot(ea, type) == -1 ? BPT_TOO_MANY : BPT_OK;
}
//--------------------------------------------------------------------------
// returns -1 if something is wrong
int pc_debmod_t::find_hwbpt_slot(ea_t ea, bpttype_t type) const
{
for ( int i=0; i < MAX_BPT; i++ )
{
if ( hwbpt_ea[i] == ea && hwbpt_type[i] == type ) // another breakpoint is here
return -1;
if ( hwbpt_ea[i] == BADADDR ) // empty slot found
return i;
}
return -1;
}
//--------------------------------------------------------------------------
bool pc_debmod_t::add_hwbpt(bpttype_t type, ea_t ea, int len)
{
int i = find_hwbpt_slot(ea, type); // get slot number
if ( i != -1 )
{
hwbpt_ea[i] = ea;
hwbpt_type[i] = type;
if ( type == BPT_EXEC )
type = 0; // exec bpts are encoded with 0 on x86
// length code used by the processor
int lenc = (len == 2) ? 1
: (len == 4) ? 3
: (len == 8) ? 2
: 0;
dr7 |= (1 << (i*2)); // enable local breakpoint
dr7 |= (type << (16+(i*4))); // set breakpoint type
dr7 |= (lenc << (18+(i*4))); // set breakpoint length
return refresh_hwbpts();
}
return false;
}
//--------------------------------------------------------------------------
bool pc_debmod_t::del_hwbpt(ea_t ea, bpttype_t type)
{
for ( int i=0; i < MAX_BPT; i++ )
{
if ( hwbpt_ea[i] == ea && hwbpt_type[i] == type )
{
hwbpt_ea[i] = BADADDR; // clean the address
dr7 &= ~(3 << (i*2)); // clean the enable bits
dr7 &= ~(0xF << (i*4+16)); // clean the length and type
return refresh_hwbpts();
}
}
return false;
}
#ifdef __NT__
//--------------------------------------------------------------------------
// Set hardware breakpoint for one thread
bool pc_debmod_t::set_hwbpts(HANDLE hThread)
{
// sure_suspend_thread(ti);
CONTEXT Context;
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
BOOL ok = GetThreadContext(hThread, &Context);
if ( !ok )
{
deberr("GetThreadContext");
return false;
}
Context.Dr0 = hwbpt_ea[0];
Context.Dr1 = hwbpt_ea[1];
Context.Dr2 = hwbpt_ea[2];
Context.Dr3 = hwbpt_ea[3];
Context.Dr6 = 0;
Context.Dr7 = dr7;
ok = SetThreadContext(hThread, &Context);
if ( !ok )
{
deberr("SetThreadContext");
}
// sure_resume_thread(ti);
return ok != FALSE;
}
//--------------------------------------------------------------------------
ea_t pc_debmod_t::is_hwbpt_triggered(thid_t id, bool is_stepping)
{
CONTEXT Context;
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
HANDLE h = get_thread_handle(id);
if ( GetThreadContext(h, &Context) )
{
for ( int i=0; i < MAX_BPT; i++ )
{
if ( (Context.Dr7 & uint32(1 << (i*2)))
&& (Context.Dr6 & uint32(1 << i)) ) // Local hardware breakpoint 'i'
{
ULONG_PTR *dr = NULL;
switch ( i )
{
case 0: dr = &Context.Dr0; break;
case 1: dr = &Context.Dr1; break;
case 2: dr = &Context.Dr2; break;
case 3: dr = &Context.Dr3; break;
}
if ( dr == NULL )
break;
if ( hwbpt_ea[i] == *dr )
{
set_hwbpts(h); // Clear the status bits
// do not report exec breakpoint if it occurs while we are stepping
if ( is_stepping && hwbpt_type[i] == BPT_EXEC )
break;
return hwbpt_ea[i];
}
//? TRACING else
// debdeb("System hardware breakpoint at %08X ???\n", *dr); //?
// what to do ?:
// reset it, and continue as if no event were received ?
// send it to IDA, and let the user setup a "stop on non-debugger hardware breakpoint" option ?
}
}
}
return BADADDR;
}
#endif // ifdef __NT__
//--------------------------------------------------------------------------
void pc_debmod_t::cleanup_hwbpts()
{
for ( int i=0; i < MAX_BPT; i++ )
{
hwbpt_ea[i] = BADADDR;
hwbpt_type[i] = bpttype_t(0);
}
dr6 = 0;
dr7 = 0x100; // exact local breakpoints
}
//--------------------------------------------------------------------------
ea_t pc_debmod_t::calc_appcall_stack(const regvals_t &regvals)
{
ea_t ea = inherited::calc_appcall_stack(regvals);
#ifndef __X86__
// do not touch the red zone (used by gcc)
ea = ea > 128 ? ea - 128 : BADADDR;
#endif
return ea;
}
//--------------------------------------------------------------------------
int pc_debmod_t::finalize_appcall_stack(
call_context_t &,
regval_map_t &,
bytevec_t &stk)
{
// pc-specific: add endless loop, so user does not execute unwanted code
// after manual appcall. we do not really need to write bpt,
// but it is easy to include it here than skip it.
// previously we reserved 'addrsize' bytes on the stack for this purpose,
// and we use 3 of them.
static const uchar bpt_and_loop[] = { 0xCC, 0xEB, 0xFE };
stk.append(bpt_and_loop, sizeof(bpt_and_loop));
return 0;
}
//--------------------------------------------------------------------------
bool pc_debmod_t::should_stop_appcall(
thid_t tid,
const debug_event_t *event,
ea_t ea)
{
if ( inherited::should_stop_appcall(tid, event, ea) )
return true;
// Check if the current instruction is a "RET" and then dereferences
// the contents of SP to find the return address. IF it matches, it is
// time to stop
regvals_t regs;
regs.resize(X86_NREGS);
do
{
// Start by reading registers
if ( dbg_read_registers(tid, X86_RC_GENERAL, regs.begin(), NULL) != DRC_OK )
break;
// Get the opcodes
uchar opcode;
if ( dbg_read_memory((ea_t)regs[pc_idx].ival, &opcode, 1, NULL) != 1 )
break;
// Check for "RET" and "RET n"
if ( opcode != 0xC3 && opcode != 0xC2 )
break;
// Dereference value at ESP
ea_t at_sp = BADADDR;
if ( dbg_read_memory((ea_t)regs[sp_idx].ival, &at_sp, sizeof(at_sp), NULL) != sizeof(at_sp) )
break;
return ea == at_sp; // time to stop!
} while ( false );
return false;
}
//--------------------------------------------------------------------------
bool pc_debmod_t::preprocess_appcall_cleanup(thid_t, call_context_t &ctx)
{
// Linux 2.6.24-19 has a bug(?):
// it doesn't clear trace flag after single-stepping
// so if we single-step and then make an appcall, we would restore eflags with TF set
// but next time we resume the program, kernel thinks that TF was set by the user
// and doesn't clear it, and so our appcall stops immediately
// to prevent that, we'll always clear trace flag before restoring eflags
if ( ctx.saved_regs.size() > sr_idx )
ctx.saved_regs[sr_idx].ival &= ~0x100;
return true; // success
}
//--------------------------------------------------------------------------
void pc_debmod_t::read_fpu_registers(
regval_t *values,
int clsmask,
const void *fptr,
size_t step)
{
const uchar *vptr = (const uchar *)fptr;
for ( int i=0; i < 8; i++,vptr+=step )
{
if ( (clsmask & X86_RC_FPU) != 0 )
{
regval_t *v = &values[R_ST0+i];
memcpy(&v->fval, vptr, 10); //-V512 v->fval underflow
v->rvtype = RVT_FLOAT;
}
if ( (clsmask & X86_RC_MMX) != 0 )
values[R_MMX0+i].set_bytes(vptr, 8);
}
}
//--------------------------------------------------------------------------
const char *pc_debmod_t::get_local_platform()
{
#ifdef __NT__
# define LOCAL_PLATFORM "win32"
#else
# ifdef __MAC__
# define LOCAL_PLATFORM "macosx"
# else
# ifdef __LINUX__
# define LOCAL_PLATFORM "linux"
# else
# define LOCAL_PLATFORM "PC_UNDEFINED"
# endif
# endif
#endif
return LOCAL_PLATFORM;
}

52
idasdk76/dbg/pc_debmod.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef __PC_DEBUGGER_MODULE__
#define __PC_DEBUGGER_MODULE__
#ifdef __NT__
# include <windows.h>
#endif
#include "pc_regs.hpp"
#include "deb_pc.hpp"
#include "debmod.h"
class pc_debmod_t: public debmod_t
{
typedef debmod_t inherited;
protected:
// Hardware breakpoints
ea_t hwbpt_ea[MAX_BPT];
bpttype_t hwbpt_type[MAX_BPT];
uint32 dr6;
uint32 dr7;
int sr_idx;
int fs_idx;
int gs_idx;
int cs_idx;
int ds_idx;
int es_idx;
int ss_idx;
public:
pc_debmod_t();
void cleanup_hwbpts();
virtual bool refresh_hwbpts() newapi { return false; }
int find_hwbpt_slot(ea_t ea, bpttype_t type) const;
bool del_hwbpt(ea_t ea, bpttype_t type);
bool add_hwbpt(bpttype_t type, ea_t ea, int len);
static const char *get_local_platform();
#ifdef __NT__
virtual bool set_hwbpts(HANDLE hThread) newapi;
ea_t is_hwbpt_triggered(thid_t id, bool is_stepping);
virtual HANDLE get_thread_handle(thid_t /*tid*/) newapi { return INVALID_HANDLE_VALUE; }
#endif
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) override;
virtual int finalize_appcall_stack(call_context_t &, regval_map_t &, bytevec_t &stk) override;
virtual ea_t calc_appcall_stack(const regvals_t &regvals) override;
virtual bool should_stop_appcall(thid_t tid, const debug_event_t *event, ea_t ea) override;
virtual bool preprocess_appcall_cleanup(thid_t tid, call_context_t &ctx) override;
virtual int get_regidx(const char *regname, int *clsmask) override;
static void read_fpu_registers(regval_t *values, int clsmask, const void *fptr, size_t step);
};
#endif

View File

@@ -0,0 +1,113 @@
// x86-specific code (compiled only on IDA side, never on the server side)
#include <dbg.hpp>
#include "pc_regs.hpp"
#include "deb_pc.hpp"
//--------------------------------------------------------------------------
//
// DEBUGGER REGISTER AND INSTRUCTION INFORMATIONS
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
#if 0
static void DEBUG_REGVALS(regval_t *values)
{
for ( int i = 0; i < qnumber(registers); i++ )
{
msg("%s = ", registers[i].name);
switch ( registers[i].dtyp )
{
case dt_qword: msg("%016LX\n", values[i].ival); break;
case dt_dword: msg("%08X\n", values[i].ival); break;
case dt_word: msg("%04X\n", values[i].ival); break;
case dt_tbyte:
for ( int j = 0; j < sizeof(regval_t); j++ )
{
if ( j == 10 )
msg(" - "); // higher bytes are not used by x86 floats
msg("%02X ", ((unsigned char*)&values[i])[j]);
}
// msg("%02X ", (unsigned short)values[i].fval[j]);
msg("\n");
break;
}
}
msg("\n");
}
#endif
//--------------------------------------------------------------------------
drc_t idaapi x86_read_registers(
thid_t thread_id,
int clsmask,
regval_t *values,
qstring *errbuf)
{
drc_t drc = s_read_registers(thread_id, clsmask, values, errbuf);
if ( drc == DRC_OK )
{
// FPU related registers
if ( (clsmask & X86_RC_FPU) != 0 )
{
for ( size_t i = 0; i < debugger.nregs; i++ )
{
const register_info_t &ri = debugger.regs(i);
if ( ri.register_class == X86_RC_FPU && ri.dtype == dt_tbyte )
{
int rc = processor_t::realcvt(&values[i].fval, &values[i].fval, 004); // load long double
if ( rc == 0 )
break; // realcvt not implemented
else if ( rc < 0 ) // error
values[i].fval.clear();
}
}
}
}
return drc;
}
//--------------------------------------------------------------------------
drc_t idaapi x86_write_register(
thid_t thread_id,
int reg_idx,
const regval_t *value,
qstring *errbuf)
{
regval_t rv = *value;
// FPU related registers
const register_info_t &ri = debugger.regs(reg_idx);
if ( ri.register_class == X86_RC_FPU && ri.dtype == dt_tbyte )
{
uchar fn[10];
int code = processor_t::realcvt(fn, &rv.fval, 014); // store long double //-V536 octal
if ( code == REAL_ERROR_FPOVER )
memcpy(&rv.fval, fn, 10); //-V512 rv.fval underflow
}
return s_write_register(thread_id, reg_idx, &rv, errbuf);
}
//--------------------------------------------------------------------------
int is_x86_valid_bpt(bpttype_t type, ea_t ea, int len)
{
if ( type != BPT_SOFT )
{
if ( (debugger.flags & DBG_FLAG_ANYSIZE_HWBPT) == 0 )
return check_x86_hwbpt(type, ea, len);
if ( type == 0 )
return BPT_BAD_TYPE;
}
return BPT_OK;
}
//--------------------------------------------------------------------------
void processor_specific_init(void)
{
}
//--------------------------------------------------------------------------
void processor_specific_term(void)
{
}

329
idasdk76/dbg/pc_regs.cpp Normal file
View File

@@ -0,0 +1,329 @@
#include "pc_regs.hpp"
//-------------------------------------------------------------------------
// NOTE: keep in sync with register_class_x86_t
const char *x86_register_classes[] =
{
"General registers",
"Segment registers",
"FPU registers",
"MMX registers",
"XMM registers",
"YMM registers",
NULL
};
//-------------------------------------------------------------------------
static const char *const eflags[] =
{
"CF", // 0
NULL, // 1
"PF", // 2
NULL, // 3
"AF", // 4
NULL, // 5
"ZF", // 6
"SF", // 7
"TF", // 8
"IF", // 9
"DF", // 10
"OF", // 11
"IOPL", // 12
"IOPL", // 13
"NT", // 14
NULL, // 15
"RF", // 16
"VM", // 17
"AC", // 18
"VIF", // 19
"VIP", // 20
"ID", // 21
NULL, // 22
NULL, // 23
NULL, // 24
NULL, // 25
NULL, // 26
NULL, // 27
NULL, // 28
NULL, // 29
NULL, // 30
NULL // 31
};
//-------------------------------------------------------------------------
static const char *const ctrlflags[] =
{
"CTRL.IM",
"CTRL.DM",
"CTRL.ZM",
"CTRL.OM",
"CTRL.UM",
"CTRL.PM",
NULL,
NULL,
"CTRL.PC",
"CTRL.PC",
"CTRL.RC",
"CTRL.RC",
"CTRL.X",
NULL,
NULL,
NULL
};
//-------------------------------------------------------------------------
static const char *const statflags[] =
{
"STAT.IE",
"STAT.DE",
"STAT.ZE",
"STAT.OE",
"STAT.UE",
"STAT.PE",
"STAT.SF",
"STAT.ES",
"STAT.C0",
"STAT.C1",
"STAT.C2",
"STAT.TOP",
"STAT.TOP",
"STAT.TOP",
"STAT.C3",
"STAT.B"
};
//-------------------------------------------------------------------------
static const char *const tagsflags[] =
{
"TAG0",
"TAG0",
"TAG1",
"TAG1",
"TAG2",
"TAG2",
"TAG3",
"TAG3",
"TAG4",
"TAG4",
"TAG5",
"TAG5",
"TAG6",
"TAG6",
"TAG7",
"TAG7"
};
//-------------------------------------------------------------------------
static const char *const xmm_format[] =
{
"XMM_4_floats",
};
//-------------------------------------------------------------------------
static const char *const ymm_format[] =
{
"YMM_8_floats",
};
//-------------------------------------------------------------------------
static const char *const mmx_format[] =
{
"MMX_8_bytes",
};
//-------------------------------------------------------------------------
static const char *const mxcsr_bits[] =
{
"IE", // 0 Invalid Operation Flag
"DE", // 1 Denormal Flag
"ZE", // 2 Divide-by-Zero Flag
"OE", // 3 Overflow Flag
"UE", // 4 Underflow Flag
"PE", // 5 Precision Flag
"DAZ", // 6 Denormals Are Zeros*
"IM", // 7 Invalid Operation Mask
"DM", // 8 Denormal Operation Mask
"ZM", // 9 Divide-by-Zero Mask
"OM", // 10 Overflow Mask
"UM", // 11 Underflow Mask
"PM", // 12 Precision Mask
"RC", // 13 Rounding Control
"RC", // 14 Rounding Control
"FZ", // 15 Flush to Zero
NULL, // 16
NULL, // 17
NULL, // 18
NULL, // 19
NULL, // 20
NULL, // 21
NULL, // 22
NULL, // 23
NULL, // 24
NULL, // 25
NULL, // 26
NULL, // 27
NULL, // 28
NULL, // 29
NULL, // 30
NULL // 31
};
//-------------------------------------------------------------------------
// General registers
#ifdef __EA64__
register_info_t r_rax = { "RAX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_rbx = { "RBX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_rcx = { "RCX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_rdx = { "RDX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_rsi = { "RSI", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_rdi = { "RDI", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_rbp = { "RBP", REGISTER_ADDRESS|REGISTER_FP, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_rsp = { "RSP", REGISTER_ADDRESS|REGISTER_SP, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_rip = { "RIP", REGISTER_ADDRESS|REGISTER_IP, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_r8 = { "R8", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_r9 = { "R9", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_r10 = { "R10", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_r11 = { "R11", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_r12 = { "R12", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_r13 = { "R13", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_r14 = { "R14", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
register_info_t r_r15 = { "R15", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
#endif
register_info_t r_eax = { "EAX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
register_info_t r_ebx = { "EBX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
register_info_t r_ecx = { "ECX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
register_info_t r_edx = { "EDX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
register_info_t r_esi = { "ESI", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
register_info_t r_edi = { "EDI", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
register_info_t r_ebp = { "EBP", REGISTER_ADDRESS|REGISTER_FP, X86_RC_GENERAL, dt_dword, NULL, 0 };
register_info_t r_esp = { "ESP", REGISTER_ADDRESS|REGISTER_SP, X86_RC_GENERAL, dt_dword, NULL, 0 };
register_info_t r_eip = { "EIP", REGISTER_ADDRESS|REGISTER_IP, X86_RC_GENERAL, dt_dword, NULL, 0 };
//-------------------------------------------------------------------------
// NOTE: keep in sync with register_x86_t
register_info_t x86_registers[] =
{
// FPU registers
{ "ST0", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
{ "ST1", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
{ "ST2", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
{ "ST3", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
{ "ST4", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
{ "ST5", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
{ "ST6", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
{ "ST7", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
{ "CTRL", 0, X86_RC_FPU, dt_word, ctrlflags, 0x1F3F },
{ "STAT", 0, X86_RC_FPU, dt_word, statflags, 0xFFFF },
{ "TAGS", 0, X86_RC_FPU, dt_word, tagsflags, 0xFFFF },
// Segment registers
{ "CS", REGISTER_CS|REGISTER_NOLF, X86_RC_SEGMENTS, dt_word, NULL, 0 },
{ "DS", REGISTER_NOLF, X86_RC_SEGMENTS, dt_word, NULL, 0 },
{ "ES", 0, X86_RC_SEGMENTS, dt_word, NULL, 0 },
{ "FS", REGISTER_NOLF, X86_RC_SEGMENTS, dt_word, NULL, 0 },
{ "GS", REGISTER_NOLF, X86_RC_SEGMENTS, dt_word, NULL, 0 },
{ "SS", REGISTER_SS, X86_RC_SEGMENTS, dt_word, NULL, 0 },
// General registers
#ifdef __EA64__
r_rax,
r_rbx,
r_rcx,
r_rdx,
r_rsi,
r_rdi,
r_rbp,
r_rsp,
r_rip,
r_r8,
r_r9,
r_r10,
r_r11,
r_r12,
r_r13,
r_r14,
r_r15,
#else
r_eax,
r_ebx,
r_ecx,
r_edx,
r_esi,
r_edi,
r_ebp,
r_esp,
r_eip,
#endif
{ "EFL", 0, X86_RC_GENERAL, dt_dword, eflags, 0x00000FD5 }, // OF|DF|IF|TF|SF|ZF|AF|PF|CF
// XMM registers
{ "XMM0", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM1", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM2", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM3", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM4", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM5", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM6", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM7", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
#ifdef __EA64__
{ "XMM8", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM9", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM10", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM11", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM12", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM13", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM14", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
{ "XMM15", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
#endif
{ "MXCSR", 0, X86_RC_XMM, dt_dword, mxcsr_bits, 0xFFFF },
// MMX registers
{ "MM0", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
{ "MM1", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
{ "MM2", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
{ "MM3", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
{ "MM4", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
{ "MM5", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
{ "MM6", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
{ "MM7", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
// YMM registers
{ "YMM0", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM1", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM2", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM3", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM4", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM5", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM6", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM7", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
#ifdef __EA64__
{ "YMM8", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM9", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM10", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM11", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM12", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM13", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM14", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
{ "YMM15", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
#endif
};
CASSERT(qnumber(x86_registers) == X86_NREGS);
//-------------------------------------------------------------------------
int x86_get_regidx(int *clsmask, const char *regname)
{
for ( size_t i = 0; i < qnumber(x86_registers); i++ )
{
if ( strieq(regname, x86_registers[i].name) )
{
if ( clsmask != NULL )
*clsmask = x86_registers[i].register_class;
return i;
}
}
return -1;
}
//-------------------------------------------------------------------------
int x86_get_regclass(int idx)
{
if ( idx >= 0 && idx < qnumber(x86_registers) )
return x86_registers[idx].register_class;
return 0;
}

172
idasdk76/dbg/pc_regs.hpp Normal file
View File

@@ -0,0 +1,172 @@
#pragma once
#include <pro.h>
#include <idd.hpp>
//-------------------------------------------------------------------------
// NOTE: keep in sync with x86_register_classes
enum register_class_x86_t
{
X86_RC_GENERAL = 0x01, // General registers
X86_RC_SEGMENTS = 0x02, // Segment registers
X86_RC_FPU = 0x04, // FPU registers
X86_RC_MMX = 0x08, // MMX registers
X86_RC_XMM = 0x10, // XMM registers
X86_RC_YMM = 0x20, // YMM registers
X86_RC_ALL = X86_RC_GENERAL
| X86_RC_SEGMENTS
| X86_RC_FPU
| X86_RC_MMX
| X86_RC_XMM
| X86_RC_YMM
};
//-------------------------------------------------------------------------
// NOTE: keep in sync with x86_registers
enum register_x86_t
{
// FPU registers
R_ST0,
R_ST1,
R_ST2,
R_ST3,
R_ST4,
R_ST5,
R_ST6,
R_ST7,
R_CTRL,
R_STAT,
R_TAGS,
// Segment registers
R_CS,
R_DS,
R_ES,
R_FS,
R_GS,
R_SS,
// General registers
R_EAX,
R_EBX,
R_ECX,
R_EDX,
R_ESI,
R_EDI,
R_EBP,
R_ESP,
R_EIP,
#ifdef __EA64__
R_R8,
R_R9,
R_R10,
R_R11,
R_R12,
R_R13,
R_R14,
R_R15,
#endif
R_EFLAGS,
// XMM registers
R_XMM0,
R_XMM1,
R_XMM2,
R_XMM3,
R_XMM4,
R_XMM5,
R_XMM6,
R_XMM7,
#ifndef __EA64__
R_LAST_XMM = R_XMM7,
#else
R_XMM8,
R_XMM9,
R_XMM10,
R_XMM11,
R_XMM12,
R_XMM13,
R_XMM14,
R_XMM15,
R_LAST_XMM = R_XMM15,
#endif
R_MXCSR,
// MMX registers
R_MMX0,
R_MMX1,
R_MMX2,
R_MMX3,
R_MMX4,
R_MMX5,
R_MMX6,
R_MMX7,
// YMM registers
R_YMM0,
R_YMM1,
R_YMM2,
R_YMM3,
R_YMM4,
R_YMM5,
R_YMM6,
R_YMM7,
#ifndef __EA64__
R_LAST_YMM = R_YMM7,
#else
R_YMM8,
R_YMM9,
R_YMM10,
R_YMM11,
R_YMM12,
R_YMM13,
R_YMM14,
R_YMM15,
R_LAST_YMM = R_YMM15,
#endif
};
// Number of registers in x86 and x64
#define X86_X64_NREGS 76
#define X86_X86_NREGS 52
#ifdef __EA64__
#define X86_NREGS X86_X64_NREGS
#else
#define X86_NREGS X86_X86_NREGS
#endif
//-------------------------------------------------------------------------
// General registers
#ifdef __EA64__
extern register_info_t r_rax;
extern register_info_t r_rbx;
extern register_info_t r_rcx;
extern register_info_t r_rdx;
extern register_info_t r_rsi;
extern register_info_t r_rdi;
extern register_info_t r_rbp;
extern register_info_t r_rsp;
extern register_info_t r_rip;
extern register_info_t r_r8;
extern register_info_t r_r9;
extern register_info_t r_r10;
extern register_info_t r_r11;
extern register_info_t r_r12;
extern register_info_t r_r13;
extern register_info_t r_r14;
extern register_info_t r_r15;
#endif
extern register_info_t r_eax;
extern register_info_t r_ebx;
extern register_info_t r_ecx;
extern register_info_t r_edx;
extern register_info_t r_esi;
extern register_info_t r_edi;
extern register_info_t r_ebp;
extern register_info_t r_esp;
extern register_info_t r_eip;
//-------------------------------------------------------------------------
extern const char *x86_register_classes[];
extern register_info_t x86_registers[X86_NREGS];
//-------------------------------------------------------------------------
int x86_get_regidx(int *clsmask, const char *regname);
int x86_get_regclass(int idx);

18
idasdk76/dbg/plugin.mak Normal file
View File

@@ -0,0 +1,18 @@
ifneq ($(wildcard ../../parse),)
CC_DEFS += ENABLE_LOWCNDS
endif
CC_INCP += ..
include ../../plugins/plugin.mak
PLUGIN_LIBS += $(L)dbg_plugin$(A)
PLUGIN_LIBS += $(L)dbg_rpc$(A)
PLUGIN_LIBS += $(L)dbg_proc$(A)
PLUGIN_LIBS += $(L)network$(A)
$(MODULES): LIBS += $(PLUGIN_LIBS)
$(MODULES): $(PLUGIN_LIBS)
ifeq ($(or $(__LINUX__),$(__MAC__)),1)
$(MODULES): STDLIBS += -ldl
endif

996
idasdk76/dbg/rpc_debmod.cpp Normal file
View File

@@ -0,0 +1,996 @@
#include <segment.hpp>
#include <err.h>
#include <network.hpp>
#include "rpc_debmod.h"
#include "dbg_rpc_hlp.h"
//-------------------------------------------------------------------------
inline drc_t unpack_drc(memory_deserializer_t &mmdsr)
{
return drc_t(mmdsr.unpack_dd());
}
//--------------------------------------------------------------------------
rpc_debmod_t::rpc_debmod_t(const char *default_platform)
: dbg_rpc_client_t(NULL)
{
nregs = debugger.nregs;
for ( int i=0; i < nregs; i++ )
{
const register_info_t &ri = debugger.regs(i);
if ( (ri.flags & REGISTER_SP) != 0 )
sp_idx = i;
if ( (ri.flags & REGISTER_IP) != 0 )
pc_idx = i;
}
bpt_code.append(debugger.bpt_bytes, debugger.bpt_size);
rpc = this;
set_platform(default_platform);
}
//--------------------------------------------------------------------------
int idaapi rpc_debmod_t::handle_ioctl( //-V524 equivalent to 'send_ioctl'
int fn,
const void *buf,
size_t size,
void **poutbuf,
ssize_t *poutsize)
{
return rpc_engine_t::send_ioctl(fn, buf, size, poutbuf, poutsize);
}
//--------------------------------------------------------------------------
inline int get_expected_addrsize(void)
{
if ( is_miniidb() )
#ifdef __EA64__
return 8;
#else
return 4;
#endif
return inf_is_64bit() ? 8 : 4;
}
//--------------------------------------------------------------------------
bool idaapi rpc_debmod_t::open_remote(
const char *hostname,
int port_number,
const char *password,
qstring *errbuf)
{
if ( hostname[0] == '\0' )
{
if ( errbuf != NULL )
*errbuf = "Please specify the hostname in Debugger, Process options";
return false;
}
rpc_packet_t *rp = NULL;
network_error = false;
client_irs = irs_new();
if ( !irs_init_client(client_irs, hostname, port_number) )
{
FAILURE:
if ( rp != NULL )
qfree(rp);
if ( errbuf != NULL )
*errbuf = irs_strerror(client_irs);
irs_term(&client_irs);
return false;
}
rp = recv_packet();
if ( rp == NULL || rp->code != RPC_OPEN ) // is this an ida debugger server?
{
dbg_rpc_client_t::dwarning("ICON ERROR\nAUTOHIDE NONE\n"
"Bogus or irresponsive remote server");
goto FAILURE;
}
memory_deserializer_t mmdsr(rp+1, rp->length);
int version = mmdsr.unpack_dd();
int remote_debugger_id = mmdsr.unpack_dd();
int easize = mmdsr.unpack_dd();
qstring errstr;
if ( version != IDD_INTERFACE_VERSION )
errstr.sprnt("protocol version is %d, expected %d", version, IDD_INTERFACE_VERSION);
else if ( remote_debugger_id != debugger.id )
errstr.sprnt("debugger id is %d, expected %d (%s)", remote_debugger_id, debugger.id, debugger.name);
else if ( easize < get_expected_addrsize() )
errstr.sprnt("address size is %d bytes, expected at least %d", easize, get_expected_addrsize());
if ( !errstr.empty() )
{
bytevec_t req = prepare_rpc_packet(RPC_OK);
req.pack_dd(false);
send_data(req);
warning("ICON ERROR\nAUTOHIDE NONE\n"
"Incompatible debugging server:\n"
"%s", errstr.c_str());
goto FAILURE;
}
qfree(rp);
bytevec_t req = prepare_rpc_packet(RPC_OK);
req.pack_dd(true);
req.pack_str(password);
send_data(req);
rp = recv_packet();
if ( rp == NULL || rp->code != RPC_OK )
goto FAILURE;
memory_deserializer_t mmdsr2(rp+1, rp->length);
bool password_ok = mmdsr2.unpack_dd() != 0;
if ( !password_ok ) // is this an ida debugger server?
{
warning("ICON ERROR\nAUTOHIDE NONE\n"
"Bad password");
goto FAILURE;
}
qfree(rp);
return true;
}
//--------------------------------------------------------------------------
int idaapi rpc_debmod_t::dbg_add_bpt(bytevec_t *, bpttype_t, ea_t, int)
{
INTERR(30114);
}
//--------------------------------------------------------------------------
int idaapi rpc_debmod_t::dbg_del_bpt(bpttype_t, ea_t, const uchar *, int)
{
INTERR(30115);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_update_lowcnds(
int *nupdated,
const lowcnd_t *lowcnds,
int nlowcnds,
qstring *errbuf)
{
ea_t ea = 0;
bytevec_t req = prepare_rpc_packet(RPC_UPDATE_LOWCNDS);
req.pack_dd(nlowcnds);
const lowcnd_t *lc = lowcnds;
for ( int i=0; i < nlowcnds; i++, lc++ )
{
req.pack_ea64(lc->ea-ea); ea = lc->ea;
req.pack_str(lc->cndbody);
if ( !lc->cndbody.empty() )
{
req.pack_dd(lc->type);
if ( lc->type != BPT_SOFT )
req.pack_dd(lc->size);
req.pack_db(lc->orgbytes.size());
req.append(lc->orgbytes.begin(), lc->orgbytes.size());
req.pack_ea64(lc->cmd.ea);
if ( lc->cmd.ea != BADADDR )
req.append(&lc->cmd, sizeof(lc->cmd));
}
}
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = unpack_drc(mmdsr);
int ret_nupdated = mmdsr.unpack_dd();
if ( nupdated != NULL )
*nupdated = ret_nupdated;
if ( errbuf != NULL && drc != DRC_NONE )
*errbuf = mmdsr.unpack_str();
qfree(rp);
return drc;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_EVAL_LOWCND);
req.pack_dd(tid);
req.pack_ea64(ea);
return send_request_get_drc_result(req, errbuf);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_update_bpts(
int *nbpts,
update_bpt_info_t *ubpts,
int nadd,
int ndel,
qstring *errbuf)
{
int skipped = 0;
update_bpt_info_t *b;
update_bpt_info_t *bend = ubpts + nadd;
for ( b=ubpts; b != bend; b++ )
if ( b->code != BPT_OK )
skipped++;
if ( skipped == nadd && ndel == 0 )
{
if ( nbpts != NULL )
*nbpts = 0; // no bpts to update
return DRC_OK;
}
bytevec_t req = prepare_rpc_packet(RPC_UPDATE_BPTS);
req.pack_dd(nadd-skipped);
req.pack_dd(ndel);
ea_t ea = 0;
for ( b=ubpts; b != bend; b++ )
{
if ( b->code == BPT_OK )
{
req.pack_ea64(b->ea-ea); ea = b->ea;
req.pack_dd(b->size);
req.pack_dd(b->type);
req.pack_dd(b->pid);
req.pack_dd(b->tid);
}
}
ea = 0;
bend += ndel;
for ( ; b != bend; b++ )
{
req.pack_ea64(b->ea-ea); ea = b->ea;
req.pack_db(b->orgbytes.size());
req.append(b->orgbytes.begin(), b->orgbytes.size());
req.pack_dd(b->type);
req.pack_dd(b->pid);
req.pack_dd(b->tid);
}
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = unpack_drc(mmdsr);
int ret_nbpts = mmdsr.unpack_dd();
if ( nbpts != NULL )
*nbpts = ret_nbpts;
bend = ubpts + nadd;
for ( b=ubpts; b != bend; b++ )
{
if ( b->code == BPT_OK )
{
b->code = mmdsr.unpack_db();
if ( b->code == BPT_OK && b->type == BPT_SOFT )
{
uchar len = mmdsr.unpack_db();
b->orgbytes.resize(len);
mmdsr.unpack_obj(b->orgbytes.begin(), len);
}
}
}
bend += ndel;
for ( ; b != bend; b++ )
b->code = mmdsr.unpack_db();
if ( errbuf != NULL && drc != DRC_NONE )
*errbuf = mmdsr.unpack_str();
return drc;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_thread_get_sreg_base(ea_t *ea, thid_t tid, int sreg_value, qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_GET_SREG_BASE);
req.pack_dd(tid);
req.pack_dd(sreg_value);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = unpack_drc(mmdsr);
if ( drc == DRC_OK )
*ea = mmdsr.unpack_ea64();
else if ( errbuf != NULL )
*errbuf = mmdsr.unpack_str();
qfree(rp);
return drc;
}
//--------------------------------------------------------------------------
void idaapi rpc_debmod_t::dbg_set_exception_info(const exception_info_t *table, int qty)
{
bytevec_t req = prepare_rpc_packet(RPC_SET_EXCEPTION_INFO);
req.pack_dd(qty);
append_exception_info(req, table, qty);
qfree(send_request_and_receive_reply(req));
}
//--------------------------------------------------------------------------
int idaapi rpc_debmod_t::dbg_open_file(const char *file, uint64 *fsize, bool readonly)
{
bytevec_t req = prepare_rpc_packet(RPC_OPEN_FILE);
req.pack_str(file);
req.pack_dd(readonly);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return -1;
memory_deserializer_t mmdsr(rp+1, rp->length);
int fn = mmdsr.unpack_dd();
if ( fn != -1 )
{
if ( fsize != NULL && readonly )
*fsize = mmdsr.unpack_dq();
}
else
{
qerrcode(mmdsr.unpack_dd());
}
qfree(rp);
return fn;
}
//--------------------------------------------------------------------------
void idaapi rpc_debmod_t::dbg_close_file(int fn)
{
bytevec_t req = prepare_rpc_packet(RPC_CLOSE_FILE);
req.pack_dd(fn);
qfree(send_request_and_receive_reply(req));
}
//--------------------------------------------------------------------------
ssize_t idaapi rpc_debmod_t::dbg_read_file(int fn, qoff64_t off, void *buf, size_t size)
{
bytevec_t req = prepare_rpc_packet(RPC_READ_FILE);
req.pack_dd(fn);
req.pack_dq(off);
req.pack_dd((uint32)size);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return -1;
memory_deserializer_t mmdsr(rp+1, rp->length);
int32 rsize = mmdsr.unpack_dd();
if ( size != rsize )
qerrcode(mmdsr.unpack_dd());
if ( rsize > 0 )
{
QASSERT(1204, rsize <= size);
mmdsr.unpack_obj(buf, rsize);
}
qfree(rp);
return rsize;
}
//--------------------------------------------------------------------------
ssize_t idaapi rpc_debmod_t::dbg_write_file(int fn, qoff64_t off, const void *buf, size_t size)
{
bytevec_t req = prepare_rpc_packet(RPC_WRITE_FILE);
req.pack_dd(fn);
req.pack_dq(off);
req.pack_buf(buf, size);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return -1;
memory_deserializer_t mmdsr(rp+1, rp->length);
int32 rsize = mmdsr.unpack_dd();
if ( size != rsize )
qerrcode(mmdsr.unpack_dd());
qfree(rp);
return rsize;
}
//--------------------------------------------------------------------------
int idaapi rpc_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len)
{
bytevec_t req = prepare_rpc_packet(RPC_ISOK_BPT);
req.pack_dd(type);
req.pack_ea64(ea);
req.pack_dd(len+1);
return send_request_get_long_result(req);
}
//--------------------------------------------------------------------------
void idaapi rpc_debmod_t::dbg_set_debugging(bool _debug_debugger)
{
debug_debugger = _debug_debugger;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_init(uint32_t *_flags2, qstring *errbuf)
{
has_pending_event = false;
poll_debug_events = false;
bytevec_t req = prepare_rpc_packet(RPC_INIT);
req.pack_dd(debugger.flags);
req.pack_dd(debug_debugger);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == nullptr )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = unpack_drc(mmdsr);
uint32_t flags2 = mmdsr.unpack_dd();
if ( _flags2 != nullptr )
*_flags2 = flags2;
if ( drc != DRC_OK && errbuf != nullptr )
*errbuf = mmdsr.unpack_str();
qfree(rp);
return drc;
}
//--------------------------------------------------------------------------
void idaapi rpc_debmod_t::dbg_term(void)
{
bytevec_t req = prepare_rpc_packet(RPC_TERM);
qfree(send_request_and_receive_reply(req));
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_get_processes(procinfo_vec_t *procs, qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_GET_PROCESSES);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
procs->qclear();
drc_t drc = unpack_drc(mmdsr);
if ( drc == DRC_OK )
extract_process_info_vec(procs, mmdsr);
else if ( errbuf != NULL )
*errbuf = mmdsr.unpack_str();
qfree(rp);
return drc;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_detach_process(void)
{
return get_drc(RPC_DETACH_PROCESS);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_start_process(
const char *path,
const char *args,
const char *startdir,
int flags,
const char *input_path,
uint32 input_file_crc32,
qstring *errbuf)
{
if ( inf_test_mode() )
flags |= DBG_HIDE_WINDOW;
bytevec_t req = prepare_rpc_packet(RPC_START_PROCESS);
req.pack_str(path);
req.pack_str(args);
req.pack_str(startdir);
req.pack_dd(flags);
req.pack_str(input_path);
req.pack_dd(input_file_crc32);
return process_start_or_attach(req, errbuf);
}
//--------------------------------------------------------------------------
gdecode_t idaapi rpc_debmod_t::dbg_get_debug_event(debug_event_t *event, int timeout_ms)
{
if ( has_pending_event )
{
verbev(("get_debug_event => has pending event, returning it\n"));
*event = pending_event;
has_pending_event = false;
poll_debug_events = false;
return GDE_ONE_EVENT;
}
gdecode_t result = GDE_NO_EVENT;
if ( poll_debug_events )
{
// do we have something waiting?
if ( irs_ready(client_irs, timeout_ms) > 0 )
{
verbev(("get_debug_event => remote has a packet for us\n"));
// get the packet - it can RPC_EVENT or RPC_MSG/RPC_WARNING/RPC_ERROR
bytevec_t empty;
rpc_packet_t *rp = send_request_and_receive_reply(empty, PREQ_GET_EVENT);
verbev(("get_debug_event => processed remote event, has=%d\n", has_pending_event));
if ( rp != NULL )
{
warning("rpc: event protocol error (rp=%p has_event=%d)", rp, has_pending_event);
return GDE_ERROR;
}
}
}
else
{
verbev(("get_debug_event => first time, send GET_DEBUG_EVENT\n"));
bytevec_t req = prepare_rpc_packet(RPC_GET_DEBUG_EVENT);
req.pack_dd(timeout_ms);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return GDE_ERROR;
memory_deserializer_t mmdsr(rp+1, rp->length);
result = gdecode_t(mmdsr.unpack_dd());
if ( result >= GDE_ONE_EVENT )
extract_debug_event(event, mmdsr);
else
poll_debug_events = true;
verbev(("get_debug_event => remote said %d, poll=%d now\n", result, poll_debug_events));
qfree(rp);
}
return result;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_attach_process(pid_t _pid, int event_id, int flags, qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_ATTACH_PROCESS);
req.pack_dd(_pid);
req.pack_dd(event_id);
req.pack_dd(flags);
return process_start_or_attach(req, errbuf);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_prepare_to_pause_process(qstring *errbuf)
{
return get_drc(RPC_PREPARE_TO_PAUSE_PROCESS, errbuf);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_exit_process(qstring *errbuf)
{
return get_drc(RPC_EXIT_PROCESS, errbuf);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_continue_after_event(const debug_event_t *event)
{
bytevec_t req = prepare_rpc_packet(RPC_CONTINUE_AFTER_EVENT);
append_debug_event(req, event);
return send_request_get_drc_result(req, NULL);
}
//--------------------------------------------------------------------------
void idaapi rpc_debmod_t::dbg_stopped_at_debug_event(
import_infos_t *,
bool dlls_added,
thread_name_vec_t *thr_names)
{
bytevec_t req = prepare_rpc_packet(RPC_STOPPED_AT_DEBUG_EVENT);
req.pack_db(dlls_added);
bool ask_thr_names = thr_names != NULL;
req.pack_db(ask_thr_names);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return;
if ( ask_thr_names )
{
memory_deserializer_t mmdsr(rp+1, rp->length);
uint32 n = mmdsr.unpack_dd();
thr_names->resize(n);
for ( uint32 i=0; i < n; ++i )
{
thread_name_t &tn = (*thr_names)[i];
tn.tid = mmdsr.unpack_dd();
tn.name = mmdsr.unpack_str();
}
}
qfree(rp);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_thread_suspend(thid_t tid)
{
return get_drc_int(RPC_TH_SUSPEND, tid);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_thread_continue(thid_t tid)
{
return get_drc_int(RPC_TH_CONTINUE, tid);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_set_resume_mode(thid_t tid, resume_mode_t resmod)
{
bytevec_t req = prepare_rpc_packet(RPC_SET_RESUME_MODE);
req.pack_dd(tid);
req.pack_dd(resmod);
return send_request_get_drc_result(req, NULL);
}
//--------------------------------------------------------------------------
// prepare bitmap of registers belonging to the specified classes
// return size of the bitmap in bits (always the total number of registers)
static int calc_regmap(bytevec_t *regmap, int clsmask)
{
int nregs = debugger.nregs;
regmap->resize((nregs+7)/8, 0);
for ( int i=0; i < nregs; i++ )
if ( (debugger.regs(i).register_class & clsmask) != 0 )
regmap->set_bit(i);
return nregs;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_read_registers(
thid_t tid,
int clsmask,
regval_t *values,
qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_READ_REGS);
req.pack_dd(tid);
req.pack_dd(clsmask);
// append additional information about the class structure
bytevec_t regmap;
int n_regs = calc_regmap(&regmap, clsmask);
req.pack_dd(n_regs);
req.append(regmap.begin(), regmap.size());
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = unpack_drc(mmdsr);
if ( drc == DRC_OK )
unpack_regvals(values, n_regs, regmap.begin(), mmdsr);
else if ( errbuf != NULL )
*errbuf = mmdsr.unpack_str();
qfree(rp);
return drc;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_write_register(
thid_t tid,
int reg_idx,
const regval_t *value,
qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_WRITE_REG);
req.pack_dd(tid);
req.pack_dd(reg_idx);
append_regvals(req, value, 1, NULL);
return send_request_get_drc_result(req, errbuf);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_get_memory_info(meminfo_vec_t &areas, qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_GET_MEMORY_INFO);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = drc_t(mmdsr.unpack_dd() + DRC_IDBSEG);
if ( drc > DRC_NONE )
{
int n = mmdsr.unpack_dd();
areas.resize(n);
for ( int i=0; i < n; i++ )
extract_memory_info(&areas[i], mmdsr);
}
else if ( errbuf != NULL )
{
*errbuf = mmdsr.unpack_str();
}
qfree(rp);
return drc;
}
//--------------------------------------------------------------------------
int idaapi rpc_debmod_t::dbg_get_scattered_image(scattered_image_t &si, ea_t base)
{
bytevec_t req = prepare_rpc_packet(RPC_GET_SCATTERED_IMAGE);
req.pack_ea64(base);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return false;
memory_deserializer_t mmdsr(rp+1, rp->length);
int result = mmdsr.unpack_dd() - 2;
if ( result > 0 )
{
int n = mmdsr.unpack_dd();
si.resize(n);
for ( int i=0; i < n; i++ )
extract_scattered_segm(&si[i], mmdsr);
}
qfree(rp);
return result;
}
//--------------------------------------------------------------------------
bool idaapi rpc_debmod_t::dbg_get_image_uuid(bytevec_t *uuid, ea_t base)
{
bytevec_t req = prepare_rpc_packet(RPC_GET_IMAGE_UUID);
req.pack_ea64(base);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return false;
memory_deserializer_t mmdsr(rp+1, rp->length);
bool result = mmdsr.unpack_dd() != 0;
if ( result )
{
int n = mmdsr.unpack_dd();
uuid->append(mmdsr.ptr, n);
}
qfree(rp);
return result;
}
//--------------------------------------------------------------------------
ea_t idaapi rpc_debmod_t::dbg_get_segm_start(ea_t base, const qstring &segname)
{
bytevec_t req = prepare_rpc_packet(RPC_GET_SEGM_START);
req.pack_ea64(base);
req.pack_str(segname.c_str());
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return false;
memory_deserializer_t mmdsr(rp+1, rp->length);
ea_t result = mmdsr.unpack_ea64();
qfree(rp);
return result;
}
//--------------------------------------------------------------------------
ssize_t idaapi rpc_debmod_t::dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_READ_MEMORY);
req.pack_ea64(ea);
req.pack_dd((uint32)size);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return -1;
memory_deserializer_t mmdsr(rp+1, rp->length);
int result = mmdsr.unpack_dd();
if ( result > 0 )
{
QASSERT(1205, result <= size);
mmdsr.unpack_obj(buffer, result);
}
else if ( errbuf != NULL )
{
*errbuf = mmdsr.unpack_str();
}
qfree(rp);
return result;
}
//--------------------------------------------------------------------------
ssize_t idaapi rpc_debmod_t::dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_WRITE_MEMORY);
req.pack_ea64(ea);
req.pack_buf(buffer, size);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return -1;
memory_deserializer_t mmdsr(rp+1, rp->length);
int result = mmdsr.unpack_dd();
if ( errbuf != NULL && result <= 0 )
*errbuf = mmdsr.unpack_str();
qfree(rp);
return result;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_update_call_stack(thid_t tid, call_stack_t *trace)
{
bytevec_t req = prepare_rpc_packet(RPC_UPDATE_CALL_STACK);
req.pack_dd(tid);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = unpack_drc(mmdsr);
if ( drc == DRC_OK )
extract_call_stack(trace, mmdsr);
qfree(rp);
return drc;
}
//--------------------------------------------------------------------------
ea_t idaapi rpc_debmod_t::dbg_appcall(
ea_t func_ea,
thid_t tid,
int stkarg_nbytes,
const struct regobjs_t *regargs,
struct relobj_t *stkargs,
struct regobjs_t *retregs,
qstring *errbuf,
debug_event_t *event,
int flags)
{
bytevec_t req = prepare_rpc_packet(RPC_APPCALL);
req.pack_ea64(func_ea);
req.pack_dd(tid);
req.pack_dd(stkarg_nbytes);
req.pack_dd(flags);
regobjs_t *rr = (flags & APPCALL_MANUAL) == 0 ? retregs : NULL;
append_appcall(req, *regargs, *stkargs, rr);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return BADADDR;
memory_deserializer_t mmdsr(rp+1, rp->length);
ea_t sp = mmdsr.unpack_ea64();
if ( sp == BADADDR )
{
if ( (flags & APPCALL_DEBEV) != 0 )
extract_debug_event(event, mmdsr);
if ( errbuf != NULL )
*errbuf = mmdsr.unpack_str();
}
else if ( (flags & APPCALL_MANUAL) == 0 )
{
if ( retregs != NULL )
extract_regobjs(retregs, true, mmdsr);
}
qfree(rp);
return sp;
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_cleanup_appcall(thid_t tid)
{
bytevec_t req = prepare_rpc_packet(RPC_CLEANUP_APPCALL);
req.pack_dd(tid);
return send_request_get_drc_result(req, NULL);
}
//--------------------------------------------------------------------------
int idaapi rpc_debmod_t::dbg_rexec(const char *cmdline)
{
bytevec_t req = prepare_rpc_packet(RPC_REXEC);
req.pack_str(cmdline);
return send_request_get_long_result(req);
}
//--------------------------------------------------------------------------
drc_t idaapi rpc_debmod_t::dbg_bin_search(
ea_t *pea,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring *errbuf)
{
bytevec_t req = prepare_rpc_packet(RPC_BIN_SEARCH);
req.pack_ea64(start_ea);
req.pack_ea64(end_ea);
// compiled_binpat_vec_t
int sz = ptns.size();
req.pack_dd(sz);
for ( compiled_binpat_vec_t::const_iterator p=ptns.begin();
p != ptns.end();
++p )
{ // compiled_binpat_t
sz = p->bytes.size();
req.pack_buf(p->bytes.begin(), sz);
sz = p->mask.size();
req.pack_buf(p->mask.begin(), sz);
sz = p->strlits.size();
req.pack_dd(sz);
for ( int i=0; i < sz; ++i )
{
req.pack_ea64(p->strlits[i].start_ea);
req.pack_ea64(p->strlits[i].end_ea);
}
req.pack_dd(p->encidx);
}
req.pack_dd(srch_flags);
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = unpack_drc(mmdsr);
if ( drc == DRC_OK )
{
if ( pea != NULL )
*pea = mmdsr.unpack_ea64();
}
else if ( drc != DRC_FAILED ) // DRC_FAILED means not found
{
if ( errbuf != NULL )
*errbuf = mmdsr.unpack_str();
}
qfree(rp);
return drc;
}
//--------------------------------------------------------------------------
drc_t rpc_debmod_t::close_remote()
{
bytevec_t req = prepare_rpc_packet(RPC_OK);
send_data(req);
irs_term(&client_irs);
network_error = false;
return DRC_OK;
}
//--------------------------------------------------------------------------
int idaapi rpc_debmod_t::get_system_specific_errno(void) const
{
return irs_get_error(client_irs);
}
//-------------------------------------------------------------------------
drc_t rpc_debmod_t::process_start_or_attach(bytevec_t &req, qstring *errbuf)
{
rpc_packet_t *rp = send_request_and_receive_reply(req);
if ( rp == NULL )
return DRC_NETERR;
memory_deserializer_t mmdsr(rp+1, rp->length);
drc_t drc = unpack_drc(mmdsr);
if ( drc > DRC_NONE )
{
extract_debapp_attrs(&debapp_attrs, mmdsr);
extract_dynamic_register_set(&idaregs, mmdsr);
}
else if ( errbuf != NULL )
{
*errbuf = mmdsr.unpack_str();
}
qfree(rp);
return drc;
}

121
idasdk76/dbg/rpc_debmod.h Normal file
View File

@@ -0,0 +1,121 @@
#ifndef __RPC_DEBUGGER_MODULE__
#define __RPC_DEBUGGER_MODULE__
#define pack_ea DONT_USE_pack_ea_USE_pack_ea64_INSTEAD
#define unpack_ea DONT_USE_unpack_ea_USE_unpack_ea64_INSTEAD
#include "debmod.h"
#include "dbg_rpc_client.h"
//---------------------------------------------------------------------------
class rpc_debmod_t
: public debmod_t,
public dbg_rpc_client_t
{
drc_t process_start_or_attach(bytevec_t &req, qstring *errbuf);
public:
rpc_debmod_t(const char *default_platform = NULL);
virtual bool idaapi open_remote(const char *hostname, int port_number, const char *password, qstring *errbuf) newapi;
drc_t close_remote();
int send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize)
{
return rpc_engine_t::send_ioctl(fn, buf, size, poutbuf, poutsize);
}
//--------------------------------------------------------------------------
inline int getint(ushort code)
{
bytevec_t req = prepare_rpc_packet((uchar)code);
return send_request_get_long_result(req);
}
drc_t get_drc_int(uchar code, int x)
{
bytevec_t req = prepare_rpc_packet(code);
req.pack_dd(x);
return send_request_get_drc_result(req, NULL);
}
inline drc_t get_drc(ushort code, qstring *errbuf=NULL)
{
bytevec_t req = prepare_rpc_packet((uchar)code);
return send_request_get_drc_result(req, errbuf);
}
//
virtual void idaapi dbg_set_debugging(bool _debug_debugger) override;
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) override;
virtual void idaapi dbg_term(void) override;
virtual drc_t idaapi dbg_get_processes(procinfo_vec_t *procs, qstring *errbuf) override;
virtual drc_t idaapi dbg_detach_process(void) override;
virtual drc_t idaapi dbg_start_process(
const char *path,
const char *args,
const char *startdir,
int flags,
const char *input_path,
uint32 input_file_crc32,
qstring *errbuf) override;
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_ms) override;
virtual drc_t idaapi dbg_attach_process(pid_t process_id, int event_id, int flags, qstring *errbuf) override;
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) override;
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) override;
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) override;
virtual void idaapi dbg_set_exception_info(const exception_info_t *info, int qty) override;
virtual void idaapi dbg_stopped_at_debug_event(import_infos_t *infos, bool dlls_added, thread_name_vec_t *thr_names) override;
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) override;
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) override;
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) override;
virtual drc_t idaapi dbg_read_registers(
thid_t thread_id,
int clsmask,
regval_t *values,
qstring *errbuf) override;
virtual drc_t idaapi dbg_write_register(
thid_t thread_id,
int reg_idx,
const regval_t *value,
qstring *errbuf) override;
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) override;
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &areas, qstring *errbuf) override;
virtual int idaapi dbg_get_scattered_image(scattered_image_t &si, ea_t base) override;
virtual bool idaapi dbg_get_image_uuid(bytevec_t *uuid, ea_t base) override;
virtual ea_t idaapi dbg_get_segm_start(ea_t base, const qstring &segname) override;
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) override;
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) override;
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) override;
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) override;
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) override;
virtual drc_t idaapi dbg_update_bpts(int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf) override;
virtual drc_t idaapi dbg_update_lowcnds(int *nupdated, const lowcnd_t *lowcnds, int nlowcnds, qstring *errbuf) override;
virtual drc_t idaapi dbg_eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf) override;
virtual int idaapi dbg_open_file(const char *file, uint64 *fsize, bool readonly) override;
virtual void idaapi dbg_close_file(int fn) override;
virtual ssize_t idaapi dbg_read_file(int fn, qoff64_t off, void *buf, size_t size) override;
virtual ssize_t idaapi dbg_write_file(int fn, qoff64_t off, const void *buf, size_t size) override;
virtual int idaapi handle_ioctl(int fn, const void *buf, size_t size, void **outbuf, ssize_t *outsize) override;
virtual int idaapi get_system_specific_errno(void) const override;
virtual drc_t idaapi dbg_update_call_stack(thid_t, call_stack_t *) override;
virtual ea_t idaapi dbg_appcall(
ea_t func_ea,
thid_t tid,
int stkarg_nbytes,
const struct regobjs_t *regargs,
struct relobj_t *stkargs,
struct regobjs_t *retregs,
qstring *errbuf,
debug_event_t *event,
int flags) override;
virtual drc_t idaapi dbg_cleanup_appcall(thid_t tid) override;
virtual int get_regidx(const char *, int *) override { INTERR(30116); }
virtual int idaapi dbg_rexec(const char *cmdline) override;
virtual drc_t idaapi dbg_bin_search(
ea_t *ea,
ea_t start_ea,
ea_t end_ea,
const compiled_binpat_vec_t &ptns,
int srch_flags,
qstring *errbuf) override;
};
#endif

224
idasdk76/dbg/server.cpp Normal file
View File

@@ -0,0 +1,224 @@
/*
IDA remote debugger server
*/
#ifdef _WIN32
// We use the deprecated inet_ntoa() function for Windows XP compatibility.
//lint -e750 local macro '' not referenced
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include <pro.h>
#include <expr.hpp>
#include "server.h"
// Provide dummy versions for tinfo copy/clear. Debugger servers do not use them
#if !defined(__NT__)
void ida_export copy_tinfo_t(tinfo_t *, const tinfo_t &) {}
void ida_export clear_tinfo_t(tinfo_t *) {}
#endif
// We don't have a kernel. Provide envvar-based debug file directory retrieval.
#if defined(__LINUX__)
static bool _elf_debug_file_directory_resolved = false;
static qstring _elf_debug_file_directory;
idaman const char *ida_export get_elf_debug_file_directory()
{
if ( !_elf_debug_file_directory_resolved )
{
if ( !qgetenv("DEBUG_FILE_DIRECTORY", &_elf_debug_file_directory) )
qgetenv("ELF_DEBUG_FILE_DIRECTORY", &_elf_debug_file_directory);
if ( _elf_debug_file_directory.empty() )
_elf_debug_file_directory = "/usr/lib/debug";
_elf_debug_file_directory_resolved = true;
}
return _elf_debug_file_directory.c_str();
}
//-------------------------------------------------------------------------
#ifdef TESTABLE_BUILD
static std::map<int,qstring> _pid_elf_debug_file_directories;
void set_elf_debug_file_directory_for_pid(int pid, const char *path)
{
_pid_elf_debug_file_directories[pid] = path;
}
//-------------------------------------------------------------------------
const char *get_elf_debug_file_directory_for_pid(int pid)
{
const char *found = NULL;
std::map<int,qstring>::const_iterator it = _pid_elf_debug_file_directories.find(pid);
if ( it != _pid_elf_debug_file_directories.end() )
found = it->second.begin();
else
found = get_elf_debug_file_directory();
return found;
}
#endif
#endif
//lint -esym(714, dump_udt) not referenced
void dump_udt(const char *, const struct udt_type_data_t &) {}
//--------------------------------------------------------------------------
// SERVER GLOBAL VARIABLES
#ifdef __SINGLE_THREADED_SERVER__
dbgsrv_dispatcher_t dispatcher(false);
static bool init_lock(void) { return true; }
bool lock_begin(void) { return true; }
bool lock_end(void) { return true; }
#else
dbgsrv_dispatcher_t dispatcher(true);
static qmutex_t g_mutex = NULL;
static bool init_lock(void) { g_mutex = qmutex_create(); return g_mutex != NULL; }
bool lock_begin(void) { return qmutex_lock(g_mutex); }
bool lock_end(void) { return qmutex_unlock(g_mutex); }
#endif
//--------------------------------------------------------------------------
dbg_rpc_handler_t *g_global_server = NULL;
//--------------------------------------------------------------------------
// perform an action (func) on all debuggers
int for_all_debuggers(debmod_visitor_t &v)
{
int code = 0;
dispatcher.clients_list->lock();
{
client_handlers_list_t::storage_t::iterator it;
for ( it = dispatcher.clients_list->storage.begin();
it != dispatcher.clients_list->storage.end();
++it )
{
dbg_rpc_handler_t *h = (dbg_rpc_handler_t *) it->first;
code = v.visit(h->get_debugger_instance());
if ( code != 0 )
break;
}
}
dispatcher.clients_list->unlock();
return code;
}
//-------------------------------------------------------------------------
dbgsrv_dispatcher_t::dbgsrv_dispatcher_t(bool multi_threaded)
: base_dispatcher_t(multi_threaded),
broken_conns_supported(false),
on_broken_conn(BCH_DEFAULT)
{
port_number = DEBUGGER_PORT_NUMBER;
}
//-------------------------------------------------------------------------
void dbgsrv_dispatcher_t::collect_cliopts(cliopts_t *out)
{
struct ida_local ns_t
{
static void _set_dpassword(const char *value, void *ud)
{
((dbgsrv_dispatcher_t *) ud)->server_password = value;
}
static void _set_broken_connections_keep_debmod(const char *, void *ud)
{
((dbgsrv_dispatcher_t *) ud)->on_broken_conn = BCH_KEEP_DEBMOD;
}
static void _set_closing_session_kill_debuggee(const char *, void *ud)
{
((dbgsrv_dispatcher_t *) ud)->on_broken_conn = BCH_KILL_PROCESS;
}
};
static const cliopt_t cliopts[] =
{
{ 'P', "password", "Password", ns_t::_set_dpassword, 1 },
};
// the following options are valid only if broken connections are supported
static const cliopt_t bc_cliopts[] =
{
{
'k',
"on-broken-connection-keep-session",
"Keep debugger session alive when connection breaks",
ns_t::_set_broken_connections_keep_debmod,
0,
},
{
'K',
"on-stop-kill-process",
"Kill debuggee when closing session",
ns_t::_set_closing_session_kill_debuggee,
0,
},
};
base_dispatcher_t::collect_cliopts(out);
for ( size_t i = 0; i < qnumber(cliopts); ++i )
out->push_back(cliopts[i]);
if ( broken_conns_supported )
for ( size_t i = 0; i < qnumber(bc_cliopts); ++i )
out->push_back(bc_cliopts[i]);
}
//-------------------------------------------------------------------------
client_handler_t *dbgsrv_dispatcher_t::new_client_handler(idarpc_stream_t *_irs)
{
dbg_rpc_handler_t *h = new dbg_rpc_handler_t(_irs, this);
h->verbose = verbose;
void *params = NULL;
#if defined(__LINUX__) && defined(TESTABLE_BUILD)
params = (void *) get_elf_debug_file_directory_for_pid; //lint !e611 cast between pointer to function type '' and pointer to object type 'void *'
#endif
h->set_debugger_instance(create_debug_session(params));
g_global_server = h;
return h;
}
//-------------------------------------------------------------------------
void dbgsrv_dispatcher_t::shutdown_gracefully(int signum)
{
base_dispatcher_t::shutdown_gracefully(signum);
term_subsystem();
}
//--------------------------------------------------------------------------
// debugger remote server - TCP/IP mode
int NT_CDECL main(int argc, const char *argv[])
{
#ifdef ENABLE_LOWCNDS
init_idc();
#endif
// call the debugger module to initialize its subsystem once
if ( !init_lock() || !init_subsystem() )
{
lprintf("Could not initialize subsystem!");
return -1;
}
qstring password;
if ( qgetenv("IDA_DBGSRV_PASSWD", &password) )
dispatcher.server_password = password;
int ida_major = IDA_SDK_VERSION / 100;
#if (IDA_SDK_VERSION % 10) == 0
int ida_minor = (IDA_SDK_VERSION % 100)/10; // 740 -> 4
#else
int ida_minor = IDA_SDK_VERSION % 100; // 741 -> 41
#endif
lprintf("IDA " SYSTEM SYSBITS " remote debug server(" __SERVER_TYPE__ ") "
"v%d.%d.%d. Hex-Rays (c) 2004-2021\n",
ida_major, ida_minor, IDD_INTERFACE_VERSION);
dispatcher.broken_conns_supported = debmod_t::reuse_broken_connections;
cliopts_t cliopts(lprintf);
dispatcher.collect_cliopts(&cliopts);
cliopts.apply(argc, argv, &dispatcher);
dispatcher.install_signal_handlers();
dispatcher.dispatch();
}

62
idasdk76/dbg/server.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef SERVER_H
#define SERVER_H
#include <network.hpp>
#ifdef __NT__
//# ifndef SIGHUP
//# define SIGHUP 1
//# endif
# define DEBUGGER_ID DEBUGGER_ID_X86_IA32_WIN32_USER
#else // not NT, i.e. UNIX
# if defined(__LINUX__)
# if defined(__ARM__)
# define DEBUGGER_ID DEBUGGER_ID_ARM_LINUX_USER
# else
# define DEBUGGER_ID DEBUGGER_ID_X86_IA32_LINUX_USER
# endif
# elif defined(__MAC__)
# if defined(__ARM__)
# define DEBUGGER_ID DEBUGGER_ID_ARM_MACOS_USER
# else
# define DEBUGGER_ID DEBUGGER_ID_X86_IA32_MACOSX_USER
# endif
# endif
# include <sys/socket.h>
# include <netinet/in.h>
#endif // !__NT__
enum broken_conn_hndl_t
{
BCH_DEFAULT,
BCH_KEEP_DEBMOD,
BCH_KILL_PROCESS,
};
struct dbgsrv_dispatcher_t : public base_dispatcher_t
{
qstring server_password;
bool broken_conns_supported;
broken_conn_hndl_t on_broken_conn;
dbgsrv_dispatcher_t(bool multi_threaded);
virtual void collect_cliopts(cliopts_t *out) override;
virtual client_handler_t *new_client_handler(idarpc_stream_t *irs) override;
virtual void shutdown_gracefully(int signum) override;
};
#include "debmod.h"
#include "dbg_rpc_hlp.h"
#include "dbg_rpc_handler.h"
// // sizeof(ea_t)==8 and sizeof(size_t)==4 servers cannot be used to debug 64-bit
// // applications. but to debug 32-bit applications, simple 32-bit servers
// // are enough and can work with both 32-bit and 64-bit versions of ida.
// // so, there is no need to build sizeof(ea_t)==8 and sizeof(size_t)==4 servers
// #if defined(__EA64__) == defined(__X86__)
// #error "Mixed mode servers do not make sense, they should not be compiled"
// #endif
#endif

17
idasdk76/dbg/server.mak Normal file
View File

@@ -0,0 +1,17 @@
SERVER_LIBS += $(L)dbg_server$(A)
SERVER_LIBS += $(L)dbg_rpc$(A)
SERVER_LIBS += $(L)dbg_proc$(A)
SERVER_LIBS += $(L)network$(A)
SERVER_LIBS += $(DUMB)
SERVER_LIBS += $(L)unicode$(A)
SERVER_LIBS += $(L)pro$(A)
SERVER_LIBS += $(L)compress$(A)
server: $(SERVERS)
$(SERVERS): LDFLAGS += $(SERVER_LDFLAGS)
$(SERVERS): STDLIBS += $(SERVER_STDLIBS)
$(SERVERS): $(SERVER_OBJS) $(SERVER_LIBS)
$(call link_exe, $(SERVER_OBJS), $(SERVER_LIBS))
$(CHECKSYMS_CMD)
$(SERVER_POSTACTION)

176
idasdk76/dbg/win32/makefile Normal file
View File

@@ -0,0 +1,176 @@
include ../../allmake.mak
GOALS-$(BUILD_IDA) += modules # target in $(IDA)module.mak
GOALS-$(BUILD_DBGSRV) += server # target in $(IDA)dbg/server.mak
.PHONY: $(GOALS-1)
all: $(GOALS-1)
#----------------------------------------------------------------------
ifdef __NT__
ifndef __X86__
SERVER = win64_remote$(B)
else
SERVER = win32_remote$(B)
endif
endif
ifdef SERVER
SERVERS += $(call server_exe,$(SERVER))
endif
#----------------------------------------------------------------------
STUB = $(call module_dll,win32_stub)
ifdef BUILD_IDA
ifeq ($(or $(IDAHOME),$(DEMO_OR_FREE)),)
MODULES += $(STUB)
endif
endif
#----------------------------------------------------------------------
USER = $(call module_dll,win32_user)
ifeq ($(and $(BUILD_IDA),$(__NT__)),1)
MODULES += $(USER)
endif
#----------------------------------------------------------------------
# we explicitly added our module targets
NO_DEFAULT_TARGETS = 1
# NOTE: all MODULES must be defined before including plugin.mak.
include ../plugin.mak
# NOTE: target-specific rules and dependencies that use variable
# expansion to name the target (such as "$(MODULE): [...]") must
# come after including plugin.mak
#----------------------------------------------------------------------
# select OBJS common to user plugin and debugger server
BASE_OBJS-$(__NT__) += $(F)win32_debmod$(O)
BASE_OBJS-$(__NT__) += $(F)win32_util$(O)
BASE_OBJS-$(__NT__) += $(F)winbase_debmod$(O)
BASE_OBJS += $(BASE_OBJS-1)
#----------------------------------------------------------------------
SERVER_OBJS += $(F)win32_server$(O)
SERVER_OBJS += $(F)tilfuncs$(O)
SERVER_OBJS += $(BASE_OBJS)
SERVER_STDLIBS += ole32.lib
SERVER_STDLIBS += oleaut32.lib
include ../server.mak
#----------------------------------------------------------------------
STUB_OBJS += $(F)win32_stub$(O)
STUB_OBJS += $(F)w32sehch$(O)
$(STUB): MODULE_OBJS += $(STUB_OBJS)
$(STUB): $(STUB_OBJS)
#----------------------------------------------------------------------
USER_OBJS += $(F)win32_user$(O)
USER_OBJS += $(F)w32sehch$(O)
USER_OBJS += $(BASE_OBJS)
$(USER): MODULE_OBJS += $(USER_OBJS)
$(USER): $(USER_OBJS)
$(USER): STDLIBS += user32.lib
#----------------------------------------------------------------------
include $(IDA)objdir.mak
# MAKEDEP dependency list ------------------
$(F)tilfuncs$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)diskio.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)ins/pc.hpp $(I)intel.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)network.hpp $(I)pro.h $(I)range.hpp \
$(I)segment.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/pe/cor.h ../../ldr/pe/corerror.h \
../../ldr/pe/corhdr.h ../../ldr/pe/mycor.h \
../../ldr/pe/pe.h ../../plugins/pdb/common.cpp \
../../plugins/pdb/cvconst.h ../../plugins/pdb/dbghelp.h \
../../plugins/pdb/dia2.h ../../plugins/pdb/idaaccess.hpp \
../../plugins/pdb/msdia.cpp ../../plugins/pdb/msdia.hpp \
../../plugins/pdb/pdb.hpp \
../../plugins/pdb/pdbaccess.hpp \
../../plugins/pdb/pdbida.hpp \
../../plugins/pdb/pdblocal.cpp \
../../plugins/pdb/pdblocal.hpp \
../../plugins/pdb/varser.hpp ../debmod.h tilfuncs.cpp \
tilfuncs.hpp
$(F)w32sehch$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)fpro.h $(I)funcs.hpp $(I)ida.hpp \
$(I)idd.hpp $(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp \
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp $(I)name.hpp \
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp w32sehch.cpp w32sehch.h
$(F)win32_debmod$(O): $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
$(I)config.hpp $(I)dbg.hpp $(I)diskio.hpp $(I)entry.hpp \
$(I)err.h $(I)exehdr.h $(I)fixup.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp \
$(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)offset.hpp $(I)pro.h $(I)prodir.h \
$(I)range.hpp $(I)segment.hpp $(I)segregs.hpp $(I)ua.hpp \
$(I)xref.hpp ../../ldr/pe/../idaldr.h \
../../ldr/pe/common.cpp ../../ldr/pe/common.h \
../../ldr/pe/pe.h ../dbg_pe_hlp.cpp ../deb_pc.hpp \
../debmod.h ../pc_debmod.h ../pc_regs.hpp \
win32_debmod.cpp win32_debmod.h win32_debmod_impl.cpp \
win32_rpc.h win32_undoc.h win32_util.hpp \
winbase_debmod.h
$(F)win32_server$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp \
$(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/pe/cor.h ../../ldr/pe/corerror.h \
../../ldr/pe/corhdr.h ../../ldr/pe/mycor.h \
../../ldr/pe/pe.h ../../plugins/pdb/cvconst.h \
../../plugins/pdb/dia2.h ../../plugins/pdb/idaaccess.hpp \
../../plugins/pdb/msdia.hpp ../../plugins/pdb/pdb.hpp \
../../plugins/pdb/pdbaccess.hpp \
../../plugins/pdb/pdbida.hpp \
../../plugins/pdb/pdblocal.hpp ../dbg_rpc_hlp.h \
../deb_pc.hpp ../debmod.h ../pc_debmod.h ../pc_regs.hpp \
tilfuncs.hpp win32_debmod.h win32_rpc.h win32_server.cpp \
win32_util.hpp winbase_debmod.h
$(F)win32_stub$(O): $(I)../ldr/pe/pe.h $(I)../plugins/pdb/pdb.hpp \
$(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/pe/pe.h ../common_local_impl.cpp \
../common_stub_impl.cpp ../dbg_rpc_client.h \
../dbg_rpc_engine.h ../dbg_rpc_hlp.h ../deb_pc.hpp \
../debmod.h ../pc_local_impl.cpp ../pc_regs.hpp \
../rpc_debmod.h w32sehch.h win32_local_impl.cpp \
win32_rpc.h win32_stub.cpp
$(F)win32_user$(O): $(I)../ldr/pe/pe.h $(I)../plugins/pdb/pdb.hpp \
$(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
../../ldr/pe/pe.h ../common_local_impl.cpp \
../common_stub_impl.cpp ../dbg_rpc_hlp.h ../deb_pc.hpp \
../debmod.h ../pc_debmod.h ../pc_local_impl.cpp \
../pc_regs.hpp w32sehch.h win32_debmod.h \
win32_local_impl.cpp win32_rpc.h win32_server_stub.cpp \
win32_user.cpp win32_util.hpp winbase_debmod.h
$(F)win32_util$(O): $(I)bytes.hpp $(I)ida.hpp $(I)idd.hpp $(I)kernwin.hpp \
$(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp $(I)netnode.hpp \
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
$(I)ua.hpp $(I)xref.hpp ../deb_pc.hpp ../debmod.h \
../pc_debmod.h ../pc_regs.hpp win32_util.cpp \
win32_util.hpp winbase_debmod.h
$(F)winbase_debmod$(O): $(I)bytes.hpp $(I)ida.hpp $(I)idd.hpp \
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp \
$(I)netnode.hpp $(I)network.hpp $(I)pro.h $(I)range.hpp \
$(I)segment.hpp $(I)ua.hpp $(I)xref.hpp ../deb_pc.hpp \
../debmod.h ../pc_debmod.h ../pc_regs.hpp win32_util.hpp \
winbase_debmod.cpp winbase_debmod.h

View File

@@ -0,0 +1,338 @@
#include <pro.h>
#include <name.hpp>
#include <kernwin.hpp>
#include <dbg.hpp>
#include <loader.hpp>
#include "w32sehch.h"
static int req_id = -1;
//-------------------------------------------------------------------------
// represents data to store on x86seh_chooser_t
struct x86seh_entry_t
{
uint32 handler = 0; // address of SEH handler
uint32 stack = 0; // address of SEH chain on the stack
x86seh_entry_t(uint32 _handler, uint32 _stack)
: handler(_handler), stack(_stack) {}
bool operator ==(const x86seh_entry_t &e) const { return handler == e.handler && stack == e.handler; }
};
DECLARE_TYPE_AS_MOVABLE(x86seh_entry_t);
//-------------------------------------------------------------------------
// non-modal exception handler chooser
struct x86seh_chooser_t : public chooser_t
{
protected:
qvector<x86seh_entry_t> list;
qstring title_;
thid_t tid;
static const int widths_[];
static const char *const header_[];
enum { ICON = 144 };
public:
// this object must be allocated using `new`
x86seh_chooser_t(thid_t tid);
virtual ~x86seh_chooser_t()
{
unhook_from_notification_point(
HT_DBG,
dbg_handler, const_cast<char *>(title));
}
ssize_t choose(uint32 addr = uint32(-1)) //lint !e1511 member hides non-virtual member
{
return ::choose(this, &addr);
}
virtual const void *get_obj_id(size_t *len) const override
{
*len = sizeof(tid);
return &tid;
}
virtual size_t idaapi get_count() const override { return list.size(); }
virtual void idaapi get_row(
qstrvec_t *cols,
int *icon_,
chooser_item_attrs_t *attrs,
size_t n) const override;
virtual cbret_t idaapi enter(size_t n) override;
// calculate the location of the item,
virtual ssize_t idaapi get_item_index(const void *item_data) const override;
virtual bool idaapi init() override;
virtual cbret_t idaapi refresh(ssize_t n) override;
ea_t get_stack_addr(int n) const;
protected:
static ssize_t idaapi dbg_handler(void *ud, int notif_code, va_list va);
};
//-------------------------------------------------------------------------
const int x86seh_chooser_t::widths_[] =
{
CHCOL_HEX | 10, // Address
30, // Name
10, // Stack
};
const char *const x86seh_chooser_t::header_[] =
{
"Address", // 0
"Name", // 1
"Stack", // 2
};
static const char seh_widget_title[] = "Structured exception handlers";
//-------------------------------------------------------------------------
inline x86seh_chooser_t::x86seh_chooser_t(thid_t tid_)
: chooser_t(CH_NOBTNS | CH_FORCE_DEFAULT | CH_CAN_REFRESH,
qnumber(widths_), widths_, header_),
tid(tid_)
{
title_.sprnt("[%04X] - %s", tid, seh_widget_title);
title = title_.c_str();
CASSERT(qnumber(widths_) == qnumber(header_));
icon = ICON;
hook_to_notification_point(
HT_DBG,
dbg_handler, const_cast<char *>(title));
}
//-------------------------------------------------------------------------
void idaapi x86seh_chooser_t::get_row(
qstrvec_t *cols_,
int *,
chooser_item_attrs_t *,
size_t n) const
{
// assert: n < list.size()
uint32 addr = list[n].handler;
qstrvec_t &cols = *cols_;
cols[0].sprnt("%08X", addr);
get_nice_colored_name(&cols[1], addr, GNCN_NOCOLOR | GNCN_NOLABEL);
// set Stack column data
cols[2].sprnt("%08X", list[n].stack);
CASSERT(qnumber(header_) == 3);
}
//-------------------------------------------------------------------------
chooser_t::cbret_t idaapi x86seh_chooser_t::enter(size_t n)
{
// assert: n < list.size()
ea_t ea = ea_t(list[n].handler);
if ( !is_code(get_flags(ea)) )
create_insn(ea);
jumpto(ea);
return cbret_t(); // nothing changed
}
//------------------------------------------------------------------------
ssize_t idaapi x86seh_chooser_t::get_item_index(const void *item_data) const
{
if ( list.empty() )
return NO_SELECTION;
const x86seh_entry_t item = *(const x86seh_entry_t *)item_data;
if ( item.handler == uint32(-1) )
return 0; // first item by default
// find `item_script` in the list
const x86seh_entry_t *p = list.find(item);
if ( p != list.end() )
return p - list.begin();
return 0; // first item by default
}
//--------------------------------------------------------------------------
bool idaapi x86seh_chooser_t::init()
{
// rebuild the handlers list
uint64 fs_sel;
ea_t fs_base;
uint32 excr_ea;
list.clear();
if ( !get_reg_val("fs", &fs_sel)
|| internal_get_sreg_base(&fs_base, tid, int(fs_sel)) <= DRC_NONE
|| read_dbg_memory(fs_base, &excr_ea, sizeof(excr_ea)) != sizeof(excr_ea) )
{
warning("Failed to build the SEH list for thread %08X", tid);
return false; // do not show the empty chooser
}
struct EXC_REG_RECORD
{
uint32 prev;
uint32 handler;
};
EXC_REG_RECORD rec;
std::set<uint32> seen;
while ( excr_ea != 0xffffffff )
{
if ( read_dbg_memory(excr_ea, &rec, sizeof(rec)) != sizeof(rec) )
break;
if ( !seen.insert(excr_ea).second )
{
msg("Circular SEH record has been detected\n");
break;
}
list.push_back(x86seh_entry_t(rec.handler, excr_ea));
excr_ea = rec.prev;
}
return true;
}
//------------------------------------------------------------------------
chooser_t::cbret_t idaapi x86seh_chooser_t::refresh(ssize_t n)
{
uint32 item_addr = uint32(-1);
if ( n >= 0 && n < list.size() )
item_addr = list[n].handler; // remember the currently selected handler
init();
if ( n < 0 )
return NO_SELECTION;
ssize_t idx = get_item_index(&item_addr);
// no need to adjust `idx` as get_item_index() returns first item by
// default
return idx;
}
//-------------------------------------------------------------------------
ea_t x86seh_chooser_t::get_stack_addr(int n) const
{
if ( n < list.size() )
return ea_t(list[n].stack);
return BADADDR;
}
//-------------------------------------------------------------------------
ssize_t idaapi x86seh_chooser_t::dbg_handler(void *ud, int code, va_list)
{
if ( code == dbg_suspend_process )
{
const char *ttl = static_cast<const char *>(ud);
refresh_chooser(ttl);
}
return 0;
}
//-------------------------------------------------------------------------
struct stkview_ah_t : public action_handler_t
{
virtual int idaapi activate(action_activation_ctx_t *ctx) override
{
if ( !ctx->chooser_selection.empty() ) // should always be the case
{
size_t idx = ctx->chooser_selection[0];
const x86seh_chooser_t *c = (x86seh_chooser_t *) ctx->source.chooser;
if ( c == nullptr )
return 0;
ea_t ea = c->get_stack_addr(idx);
if ( ea != BADADDR )
{
TWidget *w = find_widget("Stack view");
if ( w != nullptr )
{
activate_widget(w, true);
jumpto(ea);
}
}
}
return 1;
}
virtual action_state_t idaapi update(action_update_ctx_t *ctx) override
{
return ::qstrstr(ctx->widget_title.c_str(), seh_widget_title) != nullptr
? AST_ENABLE_FOR_WIDGET
: AST_DISABLE_FOR_WIDGET;
}
};
static stkview_ah_t stkview_ah;
//-------------------------------------------------------------------------
struct show_window_ah_t : public action_handler_t
{
virtual int idaapi activate(action_activation_ctx_t *) override
{
thid_t tid = get_current_thread();
x86seh_chooser_t *ch = new x86seh_chooser_t(tid);
bool ok = ch->choose() == 0;
if ( ok )
{
TWidget *w = find_widget(ch->title);
if ( w != nullptr )
{
#define ACTION_NAME "x86seh:Stack"
const action_desc_t stkview_ah_action = ACTION_DESC_LITERAL_OWNER(
ACTION_NAME,
"Follow in stack view",
&stkview_ah,
&PLUGIN,
nullptr,
nullptr,
-1,
ADF_OT_PLUGIN);
register_action(stkview_ah_action);
attach_action_to_popup(
w,
nullptr, // make permanent
ACTION_NAME);
}
}
return ok; //-V773 The function was exited without releasing the 'ch' pointer.
} //lint !e429 Custodial pointer 'ch' has not been freed or returned
virtual action_state_t idaapi update(action_update_ctx_t *) override
{
return AST_ENABLE;
}
};
static show_window_ah_t show_window_ah;
//---------------------------------------------------------------------------
void remove_x86seh_menu()
{
if ( req_id != -1 )
{
cancel_exec_request(req_id);
req_id = -1;
}
}
//---------------------------------------------------------------------------
void install_x86seh_menu()
{
// HACK: We queue this request because commdbg apparently enables the debug menus
// just after calling init_debugger().
struct uireq_install_menu_t: public ui_request_t
{
virtual bool idaapi run() override
{
if ( !inf_is_64bit() )
{
register_and_attach_to_menu(
"Debugger/Debugger windows/Stack trace",
"dbg:sehList", "SEH list", NULL, SETMENU_APP,
&show_window_ah,
&PLUGIN,
ADF_OT_PLUGIN);
}
req_id = -1;
return false;
}
};
req_id = execute_ui_requests(new uireq_install_menu_t, NULL);
}

View File

@@ -0,0 +1,7 @@
#ifndef __W32SEHCH__
#define __W32SEHCH__
void install_x86seh_menu();
void remove_x86seh_menu();
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,427 @@
#ifndef __WIN32_DEBUGGER_MODULE__
#define __WIN32_DEBUGGER_MODULE__
#include <windows.h>
#include <Tlhelp32.h>
#include "../../ldr/pe/pe.h"
#include "winbase_debmod.h"
//-V::720 It is advised to utilize the 'SuspendThread' function only when developing a debugger
// Type definitions
class win32_debmod_t;
//--------------------------------------------------------------------------
// image information
struct image_info_t
{
image_info_t() { memset(this, 0, sizeof(*this)); }
image_info_t(win32_debmod_t *);
image_info_t(win32_debmod_t *, ea_t _base, uint32 _imagesize, const qstring &_name);
image_info_t(win32_debmod_t *, const LOAD_DLL_DEBUG_INFO &i, uint32 _imagesize, const char *_name);
image_info_t(win32_debmod_t *, const modinfo_t &m);
win32_debmod_t *sess;
ea_t base;
uval_t imagesize;
qstring name;
LOAD_DLL_DEBUG_INFO dll_info;
};
// key: image base address
typedef std::map<ea_t, image_info_t> images_t;
//-------------------------------------------------------------------------
struct context_holder_t
{
bytevec_t buffer;
PCONTEXT ptr;
context_holder_t() : ptr(NULL) {}
};
//-------------------------------------------------------------------------
struct context_helper_t
{
typedef DWORD64 (WINAPI *PGETENABLEDXSTATEFEATURES)();
PGETENABLEDXSTATEFEATURES pfnGetEnabledXStateFeatures;
typedef BOOL (WINAPI *PINITIALIZECONTEXT)(PVOID Buffer, DWORD ContextFlags, PCONTEXT *Context, PDWORD ContextLength);
PINITIALIZECONTEXT pfnInitializeContext;
typedef BOOL (WINAPI *PGETXSTATEFEATURESMASK)(PCONTEXT Context, PDWORD64 FeatureMask);
PGETXSTATEFEATURESMASK pfnGetXStateFeaturesMask;
typedef PVOID (WINAPI *LOCATEXSTATEFEATURE)(PCONTEXT Context, DWORD FeatureId, PDWORD Length);
LOCATEXSTATEFEATURE pfnLocateXStateFeature;
typedef BOOL (WINAPI *SETXSTATEFEATURESMASK)(PCONTEXT Context, DWORD64 FeatureMask);
SETXSTATEFEATURESMASK pfnSetXStateFeaturesMask;
typedef BOOL (WINAPI *COPYCONTEXT)(PCONTEXT Destination, DWORD ContextFlags, PCONTEXT Source);
COPYCONTEXT pfnCopyContext;
int xstate_context_size;
bool get_xstate_context_size(int *out_ctxsz);
context_helper_t() { clear(); }
bool create_context(context_holder_t *out, int *ctxflags);
bool xstate_helpers_loaded() const { return xstate_context_size > 0; }
void clear();
};
//--------------------------------------------------------------------------
// thread information
struct thread_info_t : public CREATE_THREAD_DEBUG_INFO
{
thread_info_t(
win32_debmod_t *dm,
const CREATE_THREAD_DEBUG_INFO &i,
thid_t t,
wow64_state_t wow64_state);
win32_debmod_t *debmod;
thid_t tid; // thread id
int suspend_count;
ea_t bpt_ea;
int flags;
#define THR_TRACING 0x0001 // expecting a STEP event
#define THR_WOW64 0x0002 // is wow64 process?
#define THR_NEWNAME 0x0004 // thread was renamed
ea_t callgate_ea;
qstring name;
bool read_context(context_holder_t *out, int clsmask);
bool write_context(int clsmask, CONTEXT &ctx);
bool toggle_tbit(bool set_tbit);
bool is_tracing(void) const { return (flags & THR_TRACING) != 0; }
bool is_wow64(void) const { return (flags & THR_WOW64) != 0; }
void set_tracing(void) { flags |= THR_TRACING; }
void clr_tracing(void) { flags &= ~THR_TRACING; }
bool is_new_name(void) const { return (flags & THR_NEWNAME) != 0; }
void clr_new_name(void) { flags &= ~THR_NEWNAME; }
void set_new_name(void) { flags |= THR_NEWNAME; }
};
//--------------------------------------------------------------------------
inline thread_info_t::thread_info_t(
win32_debmod_t *dm,
const CREATE_THREAD_DEBUG_INFO &i,
thid_t t,
wow64_state_t wow64_state)
: CREATE_THREAD_DEBUG_INFO(i), tid(t), suspend_count(0), bpt_ea(BADADDR),
debmod(dm),
flags(wow64_state > 0 ? THR_WOW64 : 0),
callgate_ea(0)
{
}
//--------------------------------------------------------------------------
// Check if the context structure has valid values at the specified portion
// portion is a conbination of CONTEXT_... bitmasks
inline bool has_portion(const CONTEXT &ctx, int portion)
{
return (ctx.ContextFlags & portion & 0xFFFF) != 0;
}
//--------------------------------------------------------------------------
// (tid -> info)
struct threads_t: public std::map<DWORD, thread_info_t>
{
thread_info_t *get(DWORD tid)
{
const iterator it = find(tid);
if ( it == end() )
return NULL;
return &it->second;
}
};
//--------------------------------------------------------------------------
typedef qvector<thread_info_t> threadvec_t;
//--------------------------------------------------------------------------
// structure for the internal breakpoint information for threads
struct internal_bpt_info_t
{
int count; // number of times this breakpoint is 'set'
uchar orig_bytes[BPT_CODE_SIZE]; // original byte values
};
typedef std::map<ea_t, internal_bpt_info_t> bpt_info_t;
//--------------------------------------------------------------------------
typedef int (*process_cb_t)(debmod_t *, PROCESSENTRY32 *pe32, void *ud);
typedef int (*module_cb_t)(debmod_t *, MODULEENTRY32 *me32, void *ud);
//----------------------------------------------------------------------------
// A live PDB session, that will be used remotely (typically by non-windows machines).
struct pdb_remote_session_t;
void close_pdb_remote_session(pdb_remote_session_t *);
// Wow64-specific events
#ifndef STATUS_WX86_BREAKPOINT
# define STATUS_WX86_BREAKPOINT 0x4000001f
#endif
#ifndef STATUS_WX86_SINGLE_STEP
# define STATUS_WX86_SINGLE_STEP 0x4000001e
#endif
//-------------------------------------------------------------------------
struct machine_thread_state_t;
struct machine_float_state_t;
//--------------------------------------------------------------------------
class win32_debmod_t : public winbase_debmod_t
{
typedef winbase_debmod_t inherited;
regctx_t *reg_ctx;
gdecode_t get_debug_event(debug_event_t *event, int timeout_ms);
void check_thread(bool must_be_main_thread) const;
void add_thread(const CREATE_THREAD_DEBUG_INFO &thr_info, thid_t tid);
void install_callgate_workaround(thread_info_t *ti, const debug_event_t *event);
int describe_stack_segment(
thid_t tid,
images_t &thr_ranges,
images_t &cls_ranges,
const _NT_TIB &tib,
const char *pref);
void update_thread_names(thread_name_vec_t *thr_names);
bool get_pe_exports_from_path(
const char *path,
linput_t *li,
ea_t imagebase,
name_info_t &ni,
const char *exported_name=NULL) const;
void _term_reg_ctx(void);
public:
// debugged process information
qstring process_path;
HANDLE thread_handle;
HANDLE redirin_handle;
HANDLE redirout_handle;
attach_status_t attach_status;
HANDLE attach_evid;
int8 expecting_debug_break;
bool stop_at_ntdll_bpts;
images_t curproc; // image of the running process
images_t dlls; // list of loaded DLLs
images_t images; // list of detected PE images
images_t thread_ranges; // list of ranges related to threads
images_t class_ranges; // list of ranges related to class names
easet_t dlls_to_import; // list of dlls to import information from
modinfo_t binary_to_import; // executable to import information from
bpt_info_t thread_bpts;
threads_t threads;
// ID of a thread for which we must emulate a STEP event on XP (using a breakpoint)
thid_t winxp_step_thread;
CREATE_PROCESS_DEBUG_INFO cpdi;
debug_event_t *in_event; // current debug event
bool fake_suspend_event;
bool exiting;
bool pause_requested;
procinfo_vec_t processes;
// threads suspended by the fiber created for restoring broken connections
threadvec_t _suspended_threads;
// event to wait until the broken connection is completely restored
HANDLE broken_event_handle;
context_helper_t context_helper;
// Module specific methods, to be implemented
virtual void idaapi dbg_set_debugging(bool _debug_debugger) override;
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) override;
virtual void idaapi dbg_term(void) override;
virtual drc_t idaapi dbg_detach_process(void) override;
virtual drc_t idaapi dbg_start_process(
const char *path,
const char *args,
const char *startdir,
int flags,
const char *input_path,
uint32 input_file_crc32,
qstring *errbuf) override;
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_ms) override;
virtual drc_t idaapi dbg_attach_process(
pid_t process_id,
int event_id,
int flags,
qstring *errbuf) override;
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) override;
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) override;
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) override;
virtual void idaapi dbg_stopped_at_debug_event(
import_infos_t *infos,
bool dlls_added,
thread_name_vec_t *thr_names) override;
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) override;
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) override;
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) override;
virtual drc_t idaapi dbg_read_registers(
thid_t thread_id,
int clsmask,
regval_t *values,
qstring *errbuf) override;
virtual drc_t idaapi dbg_write_register(
thid_t thread_id,
int reg_idx,
const regval_t *value,
qstring *errbuf) override;
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) override;
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &ranges, qstring *errbuf) override;
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) override;
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) override;
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) override;
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) override;
virtual int idaapi handle_ioctl(int fn, const void *buf, size_t size, void **outbuf, ssize_t *outsize) override;
//
win32_debmod_t();
~win32_debmod_t() { cleanup(); _term_reg_ctx(); }
virtual void init_reg_ctx(void) override;
virtual void term_reg_ctx(void) override;
bool get_thread_state(
context_holder_t *out_ctxh,
machine_thread_state_t *out_regs,
machine_float_state_t *out_floats,
thid_t tid,
int clsmask);
bool set_thread_state(
const machine_thread_state_t &regs,
const machine_float_state_t &floats,
const context_holder_t &ctxh,
thid_t tid,
int clsmask);
void handle_pdb_thread_request(void *data);
uint32 calc_imagesize(eanat_t base);
bool get_filename_for(
char *buf,
size_t bufsize,
eanat_t image_name_ea,
bool use_unicode,
eanat_t image_base);
ea_t get_dll_export(
const images_t &dlls,
ea_t imagebase,
const char *exported_name);
bool create_process(
const char *path,
const char *args,
const char *startdir,
bool is_gui,
bool hide_window,
PROCESS_INFORMATION *ProcessInformation);
void show_debug_event(const DEBUG_EVENT &ev);
ssize_t _read_memory(eanat_t ea, void *buffer, size_t size, bool suspend = false);
ssize_t _write_memory(eanat_t ea, const void *buffer, size_t size, bool suspend = false);
int rdmsr(int reg, uint64 *value);
int wrmsr(int reg, uint64 value);
int kldbgdrv_access_msr(struct SYSDBG_MSR *msr, bool write);
// !! OVERWRITTEN METHODS !!
bool refresh_hwbpts();
// Utility methods
gdecode_t handle_exception(debug_event_t *event,
const EXCEPTION_RECORD &er,
bool was_thread_bpt,
bool firsttime);
ssize_t access_memory(eanat_t ea, void *buffer, ssize_t size, bool write, bool suspend);
inline void resume_all_threads(bool raw = false);
inline void suspend_all_threads(bool raw = false);
size_t add_dll(image_info_t &ii);
bool module_present(const char *modname);
HANDLE get_thread_handle(thid_t tid);
static int get_dmi_cb(debmod_t *sess, MODULEENTRY32 *me32, void *ud);
void get_debugged_module_info(modinfo_t *dmi);
int for_each_module(DWORD pid, module_cb_t module_cb, void *ud);
bool myCloseHandle(HANDLE &h);
void cleanup(void);
void restore_original_bytes(ea_t ea, bool really_restore = true);
int save_original_bytes(ea_t ea);
bool set_thread_bpt(thread_info_t &ti, ea_t ea);
bool del_thread_bpt(thread_info_t &ti, ea_t ea);
bool del_thread_bpts(ea_t ea);
bool has_bpt_at(ea_t ea);
bool can_access(ea_t addr);
ea_t get_kernel_bpt_ea(ea_t ea, thid_t tid);
void create_attach_event(debug_event_t *event, bool attached);
void create_start_event(debug_event_t *event);
bool check_for_hwbpt(debug_event_t *event, bool is_stepping=false);
ea_t get_region_info(ea_t ea, memory_info_t *info);
bool get_dll_exports(
const images_t &dlls,
ea_t imagebase,
name_info_t &ni,
const char *exported_name = NULL);
bool get_filename_from_process(
eanat_t name_ea,
bool is_unicode,
char *buf,
size_t bufsize);
bool get_debug_string(const DEBUG_EVENT &ev, char *buf, size_t bufsize);
int add_thread_ranges(
thid_t tid,
images_t &thread_ranges,
images_t &class_ranges);
ea_t get_pe_header(eanat_t imagebase, peheader_t *nh);
bool get_pe_export_name_from_process(
eanat_t imagebase,
char *name,
size_t namesize);
void show_exception_record(const EXCEPTION_RECORD &er, int level=0);
eanat_t pstos0(eanat_t ea);
eanat_t s0tops(eanat_t ea);
bool prepare_to_stop_process(debug_event_t *, const threads_t &);
bool disable_hwbpts();
bool enable_hwbpts();
bool may_write(ea_t ea);
LPVOID correct_exe_image_base(LPVOID base);
bool clear_tbit(thread_info_t &th);
void enqueue_event(const debug_event_t &ev, queue_pos_t pos);
void suspend_running_threads(threadvec_t &suspended);
void resume_suspended_threads(threadvec_t suspended) const;
bool reopen_threads(void);
virtual bool idaapi write_registers(
thid_t thread_id,
int start,
int count,
const regval_t *values) override;
virtual bool idaapi dbg_prepare_broken_connection(void) override;
virtual bool idaapi dbg_continue_broken_connection(pid_t pid) override;
qvector<pdb_remote_session_t*> pdb_remote_sessions;
pdb_remote_session_t *get_pdb_session(int id);
void delete_pdb_session(int id);
protected:
virtual int dbg_freeze_threads_except(thid_t tid) override;
virtual int dbg_thaw_threads_except(thid_t tid) override;
};
ea_t s0tops(ea_t ea);
#endif

View File

@@ -0,0 +1,717 @@
//
//
// This file contains win32 specific implementations of win32_debmod class
//
//
#include <diskio.hpp>
#include "win32_rpc.h"
#include "win32_undoc.h"
#include "dbg_pe_hlp.cpp"
struct impfunc_t
{
const char *name;
void *fptr;
};
#define IMPFUNC(x) { TEXT(#x), &x }
//lint -esym(843,ntdll) -esym(844,ntdll) could be const
static HMODULE ntdll = NULL;
static NtSystemDebugControl_t *NtSystemDebugControl;
static NtLoadDriver_t *NtLoadDriver;
static NtUnloadDriver_t *NtUnloadDriver;
static RtlAdjustPrivilege_t *RtlAdjustPrivilege;
static NtCreateFile_t *NtCreateFile;
static NtDeviceIoControlFile_t *NtDeviceIoControlFile;
static const impfunc_t ntfuncs[] =
{
IMPFUNC(NtSystemDebugControl),
IMPFUNC(NtLoadDriver),
IMPFUNC(NtUnloadDriver),
IMPFUNC(RtlAdjustPrivilege),
IMPFUNC(NtCreateFile),
IMPFUNC(NtDeviceIoControlFile),
};
// To read MSRs, we use a local kernel debugger driver provided by Microsoft.
//lint -esym(843,DriverHandle,DriverPath,DriverName) could be const
//lint -esym(844,DriverHandle) could be pointing to const
static HANDLE DriverHandle = NULL;
static UNICODE_STRING DriverPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\SERVICES\\kldbgdrv");
static UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Device\\kldbgdrv");
//--------------------------------------------------------------------------
// PE COMMON HELPER FUNCTIONS
#define lread myread // since we can't use loader_failure()
inline void myread(linput_t *li, void *buf, size_t size)
{
int bytes_read = qlread(li, buf, size);
if ( bytes_read != size )
{
int saved_code = qerrcode();
const char *errmsg = qerrstr();
uint64 pos = qltell(li) - bytes_read;
static const char *const format =
"Read error: %s\n"
"(file position 0x%" FMT_64 "X, wanted 0x%" FMT_Z "X bytes, read 0x%X)";
error(format,
saved_code ? errmsg : "read past end of file",
pos,
size,
bytes_read);
}
}
#include "../../ldr/pe/common.cpp"
#define GetMappedFileName_Name "GetMappedFileNameW"
#define GetModuleFileNameEx_Name "GetModuleFileNameExW"
// function prototypes
typedef DWORD (WINAPI *GetMappedFileName_t)(HANDLE hProcess, LPVOID lpv, LPWSTR lpFilename, DWORD nSize);
typedef DWORD (WINAPI *GetModuleFileNameEx_t)(HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
// functions pointers
//lint -esym(843,_GetMappedFileName,_GetModuleFileNameEx) could be const
static GetMappedFileName_t _GetMappedFileName = NULL;
static GetModuleFileNameEx_t _GetModuleFileNameEx = NULL;
// dynamic linking information for PSAPI functions
//lint -esym(843,hPSAPI) -esym(844,hPSAPI) could be const
static HMODULE hPSAPI = NULL;
// dw32 support
//lint -esym(843,system_teb_size) could be const
static DWORD system_teb_size = MEMORY_PAGE_SIZE;
//--------------------------------------------------------------------------
LPVOID win32_debmod_t::correct_exe_image_base(LPVOID base)
{
return base;
}
//--------------------------------------------------------------------------
eanat_t win32_debmod_t::s0tops(eanat_t ea)
{
return ea;
}
//--------------------------------------------------------------------------
eanat_t win32_debmod_t::pstos0(eanat_t ea)
{
return ea;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::prepare_to_stop_process(debug_event_t *, const threads_t &)
{
return true;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::disable_hwbpts()
{
for ( page_bpts_t::iterator p = page_bpts.begin(); p != page_bpts.end(); ++p )
dbg_enable_page_bpt(p, false);
return true;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::enable_hwbpts()
{
for ( page_bpts_t::iterator p = page_bpts.begin(); p != page_bpts.end(); ++p )
dbg_enable_page_bpt(p, true);
return true;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::may_write(ea_t /*ea*/)
{
return true;
}
//--------------------------------------------------------------------------
int win32_debmod_t::describe_stack_segment(
thid_t tid,
images_t &thr_ranges,
images_t &cls_ranges,
const _NT_TIB &tib,
const char *pref) // "x64" for x64 part of wow64 processes
{
int cnt = 1;
char name[MAXSTR];
asize_t size = EA_T(tib.StackBase) - EA_T(tib.StackLimit);
qsnprintf(name, sizeof(name), "%sStack[%08X]", pref, tid);
image_info_t ii_stack(this, EA_T(tib.StackLimit), size, name);
thr_ranges.insert(std::make_pair(ii_stack.base, ii_stack));
ii_stack.name = "STACK";
cls_ranges.insert(std::make_pair(ii_stack.base, ii_stack));
// verify a Stack PAGE_GUARD page exists
ea_t ea_guard = ii_stack.base - MEMORY_PAGE_SIZE;
MEMORY_BASIC_INFORMATION MemoryBasicInformation;
if ( VirtualQueryEx(process_handle, (LPCVOID)(size_t)ea_guard,
&MemoryBasicInformation, sizeof(MemoryBasicInformation)) )
{
if ( MemoryBasicInformation.Protect & PAGE_GUARD ) // a Stack PAGE_GUARD exists
{
qsnprintf(name, sizeof(name), "%sStack_PAGE_GUARD[%08X]", pref, tid);
image_info_t ii_guard(this, ea_guard, MEMORY_PAGE_SIZE, name);
thr_ranges.insert(std::make_pair(ii_guard.base, ii_guard));
ii_guard.name = "STACK";
cls_ranges.insert(std::make_pair(ii_guard.base, ii_guard));
cnt++;
}
}
return cnt;
}
//--------------------------------------------------------------------------
int win32_debmod_t::add_thread_ranges(
thid_t tid,
images_t &thr_ranges,
images_t &cls_ranges)
{
thread_info_t *ti = threads.get(tid);
if ( ti == NULL )
return 0;
// This structure is specific to NT, but stack related records are Win9X compatible
_NT_TIB tib;
ea_t ea_tib = EA_T(ti->lpThreadLocalBase);
if ( _read_memory(ea_tib, &tib, sizeof(tib)) != sizeof(tib) ) // read the TIB
return 0;
// additional test: we verify that TIB->Self contains the TIB's linear address
if ( EA_T(tib.Self) != ea_tib )
return false;
// add TIB range
char name[MAXSTR];
qsnprintf(name, sizeof(name), "TIB[%08X]", tid);
// we suppose the whole page is reserved for the TIB
image_info_t ii_tib(this, ea_tib, system_teb_size, name);
thr_ranges.insert(std::make_pair(ii_tib.base, ii_tib));
int cnt = 0;
const char *pref = "";
if ( check_wow64_process() == WOW64_YES )
{
// Note: This works for Windows versions <= 8.1
// The offset of the 32-bit TEB address within the 64-bit TEB is 0.
// This can be used to directly access the 32-bit TEB of a WOW64 thread
ea_t wow64_tib_ea = *(uint32*)&tib;
struct _NT_TIB32
{
DWORD ExceptionList;
DWORD StackBase;
DWORD StackLimit;
DWORD SubSystemTib;
DWORD FiberData;
DWORD ArbitraryUserPointer;
DWORD Self;
};
_NT_TIB32 tib32;
if ( _read_memory(wow64_tib_ea, &tib32, sizeof(tib32)) == sizeof(tib32) )
{
_NT_TIB tib2;
tib2.StackBase = (PVOID)(eanat_t)tib32.StackBase;
tib2.StackLimit = (PVOID)(eanat_t)tib32.StackLimit;
cnt += describe_stack_segment(tid, thr_ranges, cls_ranges, tib2, pref);
}
pref = "x64";
}
// add stack range
cnt += describe_stack_segment(tid, thr_ranges, cls_ranges, tib, pref);
return cnt;
}
//--------------------------------------------------------------------------
// Get PE header
// In: ea=DLL imagebase, nh=buffer to keep the answer
// child==true:ea is an address in the child process
// child==false:ea is an address in the the debugger itself
// Returns: offset to the headers, BADADDR means failure
ea_t win32_debmod_t::get_pe_header(eanat_t ea, peheader_t *nh)
{
uint32 offset = 0;
uint32 magic;
if ( _read_memory(ea, &magic, sizeof(magic)) != sizeof(magic) )
return BADADDR;
if ( ushort(magic) == MC2('M','Z') )
{
if ( _read_memory(ea+PE_PTROFF, &offset, sizeof(offset)) != sizeof(offset) )
return BADADDR;
}
peheader64_t pe64;
if ( _read_memory(ea+offset, &pe64, sizeof(pe64)) != sizeof(pe64) )
return BADADDR;
if ( !pe64_to_pe(*nh, pe64, true, true) )
return BADADDR;
if ( nh->signature != PEEXE_ID )
return BADADDR;
return offset;
}
//--------------------------------------------------------------------------
// calculate dll image size
// since we could not find anything nice, we just look
// at the beginning of the DLL module in the memory and extract
// correct value from the file header
uint32 win32_debmod_t::calc_imagesize(eanat_t base)
{
peheader_t nh;
ea_t peoff = get_pe_header(base, &nh);
if ( peoff == BADADDR )
return 0;
return nh.imagesize;
}
//--------------------------------------------------------------------------
bool win32_debmod_t::create_process(
const char *path,
const char *args,
const char *startdir,
bool is_gui,
bool hide_window,
PROCESS_INFORMATION *ProcessInformation)
{
linput_t *li = open_linput(path, false);
if ( li == NULL )
return false;
pe_loader_t pl;
pl.read_header(li, true);
close_linput(li);
#ifndef __EA64__
if ( pl.pe.is_pe_plus() )
{
dwarning("AUTOHIDE NONE\nPlease use ida64 to debug 64-bit applications");
SetLastError(ERROR_NOT_SUPPORTED);
return false;
}
#endif
#ifdef __X86__
if ( pl.pe.is_pe_plus() )
{
static const char server_name[] = "win64_remote64.exe";
if ( ask_yn(ASKBTN_YES,
"AUTOHIDE REGISTRY\nHIDECANCEL\n"
"Debugging 64-bit applications is only possible with the %s server.\n"
"Launch it now?",
server_name) == ASKBTN_YES )
{
do
{
// Switch to the remote win32 debugger
if ( !load_debugger("win32_stub", true) )
{
warning("Failed to switch to the remote windows debugger!");
break;
}
// Form the server path
char server_exe[QMAXPATH];
qmakepath(server_exe, sizeof(server_exe), idadir(NULL), server_name, NULL);
// Try to launch the server
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
if ( hide_window )
{
si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; /* SW_FORCEMINIMIZE ? */
}
if ( !::CreateProcess(server_exe, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) )
{
warning("Failed to run the 64-bit remote server!");
break;
}
// Set the remote debugging options: localhost
set_remote_debugger("localhost", "", -1);
// Notify the user
info("Debugging server has been started, please try debugging the program again.");
} while ( false );
}
SetLastError(ERROR_NOT_SUPPORTED);
return false;
}
#endif // __X86__
// Empty directory means our directory
if ( startdir != NULL && startdir[0] == '\0' )
startdir = NULL;
// Args passed as empty string?
if ( args != NULL && args[0] == '\0' )
args = NULL;
launch_process_params_t lpp;
lpp.flags |= LP_TRACE | LP_PATH_WITH_ARGS;
if ( !is_gui )
lpp.flags |= LP_NEW_CONSOLE;
if ( hide_window )
lpp.flags |= LP_HIDE_WINDOW;
lpp.path = path;
lpp.args = args;
lpp.startdir = startdir;
lpp.info = ProcessInformation;
qstring errbuf;
if ( launch_process(lpp, &errbuf) == NULL )
{
dwarning("AUTOHIDE NONE\n%s", errbuf.c_str());
return false;
}
return true;
}
//--------------------------------------------------------------------------
void term_win32_subsystem(void)
{
if ( hPSAPI != NULL )
{
FreeLibrary(hPSAPI);
hPSAPI = NULL;
}
if ( DriverHandle != NULL )
{
CloseHandle(DriverHandle);
DriverHandle = NULL;
NtUnloadDriver(&DriverPath);
}
}
//--------------------------------------------------------------------------
void init_win32_subsystem(void)
{
ntdll = GetModuleHandle(TEXT("ntdll.dll"));
if ( ntdll != NULL )
{
for ( int i=0; i < qnumber(ntfuncs); i++ )
*(FARPROC*)ntfuncs[i].fptr = GetProcAddress(ntdll, ntfuncs[i].name);
}
// load the library
hPSAPI = LoadLibrary(TEXT("psapi.dll"));
if ( hPSAPI != NULL )
{
// find the needed functions
*(FARPROC*)&_GetMappedFileName = GetProcAddress(hPSAPI, TEXT(GetMappedFileName_Name));
*(FARPROC*)&_GetModuleFileNameEx = GetProcAddress(hPSAPI, TEXT(GetModuleFileNameEx_Name));
if ( _GetMappedFileName == NULL )
{
FreeLibrary(hPSAPI);
hPSAPI = NULL;
}
}
}
//--------------------------------------------------------------------------
bool win32_debmod_t::can_access(ea_t addr)
{
char dummy;
return access_memory(addr, &dummy, 1, false, false) == 1;
}
//--------------------------------------------------------------------------
// return the address of all names exported by a DLL in 'ni'
// if 'exported_name' is given, only the address of this exported name will be returned in 'ni'
bool win32_debmod_t::get_pe_exports_from_path(
const char *path,
linput_t *li,
ea_t imagebase,
name_info_t &ni,
const char *exported_name) const
{
// prepare nice name prefix for exported functions names
char prefix[MAXSTR];
qstrncpy(prefix, qbasename(path), sizeof(prefix));
char *ptr = strrchr(prefix, '.');
if ( ptr != NULL )
*ptr = '\0';
qstrlwr(prefix);
pe_loader_t pl;
if ( !pl.read_header(li) )
return false;
struct export_reader_t : public pe_export_visitor_t
{
const char *prefix;
ea_t imagebase;
name_info_t &ni;
const char *exported_name;
export_reader_t(const char *pfx, ea_t base, name_info_t &_ni, const char *exname)
: prefix(pfx), imagebase(base), ni(_ni), exported_name(exname) {}
int idaapi visit_export(uint32 rva, uint32 ord, const char *name, const char *)
{
ea_t fulladdr = imagebase + rva;
if ( exported_name != NULL )
{
if ( strcmp(exported_name, name) == 0 )
{
ni.addrs.push_back(fulladdr);
return 1;
}
}
else
{
qstring n2;
if ( name[0] == '\0' )
n2.sprnt("%s_%u", prefix, ord);
else
n2.sprnt("%s_%s", prefix, name);
ni.addrs.push_back(fulladdr);
ni.names.push_back(n2.extract());
}
return 0;
}
};
export_reader_t er(prefix, imagebase, ni, exported_name);
return pl.process_exports(li, er) >= 0;
}
//--------------------------------------------------------------------------
// return the address of all names exported by a DLL in 'ni'
// if 'exported_name' is given, only the address of this exported name will be returned in 'ni'
bool win32_debmod_t::get_dll_exports(
const images_t &loaded_dlls,
ea_t imagebase,
name_info_t &ni,
const char *exported_name)
{
char prefix[MAXSTR];
images_t::const_iterator p = loaded_dlls.find(imagebase);
if ( p == loaded_dlls.end() )
{
dwarning("get_dll_exports: can't find dll name for imagebase %a", imagebase);
return false;
}
char dname[MAXSTR];
const char *dllname = p->second.name.c_str();
qstrncpy(dname, dllname, sizeof(dname));
if ( debapp_attrs.addrsize == 4 )
replace_system32(dname, MAXSTR);
linput_t *li = open_linput(dname, false);
if ( li == NULL )
{
// sysWOW64: ntdll32.dll does not exist but there is a file called ntdll.dll
if ( stricmp(qbasename(dllname), "ntdll32.dll") != 0 )
return false;
if ( qisabspath(dllname) )
{
qstrncpy(prefix, dllname, sizeof(prefix));
char *fname = qbasename(prefix);
qstrncpy(fname, "ntdll.dll", sizeof(prefix)-(fname-prefix));
dllname = prefix;
}
else
{
#ifdef __X86__
// TODO: On X86 there might by file redirection active on a X64 host
// Therefore we will load on such system always 32 bit DLL, as we can
// access 64 bit one without disabling the redirection
dllname = "C:\\Windows\\System32\\ntdll.dll";
#else
#ifndef __EA64__
dllname = "C:\\Windows\\SysWOW64\\ntdll.dll";
#else
dllname = debapp_attrs.addrsize != 4 ? "C:\\Windows\\System32\\ntdll.dll" : "C:\\Windows\\SysWOW64\\ntdll.dll";
#endif
#endif
}
li = open_linput(dllname, false);
if ( li == NULL )
return false;
}
bool ok = get_pe_exports_from_path(dllname, li, imagebase, ni, exported_name);
close_linput(li);
return ok;
}
//--------------------------------------------------------------------------
// get name from export directory in PE image in debugged process
bool win32_debmod_t::get_pe_export_name_from_process(
eanat_t imagebase,
char *name,
size_t namesize)
{
peheader_t pe;
ea_t peoff = get_pe_header(imagebase, &pe);
if ( peoff != BADADDR && pe.expdir.rva != 0 )
{
eanat_t ea = imagebase + pe.expdir.rva;
peexpdir_t expdir;
if ( _read_memory(ea, &expdir, sizeof(expdir)) == sizeof(expdir) )
{
ea = imagebase + expdir.dllname;
name[0] = '\0';
_read_memory(ea, name, namesize); // don't check the return code because
// we might have read more than necessary
if ( name[0] != '\0' )
return true;
}
}
return false;
}
//--------------------------------------------------------------------------
// Read/write a model specific register using the driver provided by WinDbg.
// The following requirements are imposed by this code:
// - debugger module should be run with admin privileges
// - System must be loaded with /debug switch (use bcdedit.exe to turn it on)
// - Windbg local kernel debugging should be used at least once
// This code is based on a sample kindly provided by Alex Ionescu.
int win32_debmod_t::kldbgdrv_access_msr(SYSDBG_MSR *msr, bool write)
{
NTSTATUS code;
IO_STATUS_BLOCK IoStatusBlock;
if ( DriverHandle == NULL )
{
//
// Acquire 'load driver' privilege
//
BOOLEAN Old;
code = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, TRUE, FALSE, &Old);
if ( FAILED(code) )
{
dwarning("AUTOHIDE NONE\n"
"Failed to acquire 'load driver' privilege, please run as admin!\n"
"Error: %s\n", winerr(code));
return code;
}
//
// And need this for the driver to accept our commands
// Additionally, system must be booted in /DEBUG mode
//
code = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &Old);
if ( FAILED(code) )
{
dwarning("AUTOHIDE NONE\n"
"Failed to acquire 'debug' privilege, is system booted in /debug mode?\n"
"Error: %s\n", winerr(code));
return code;
}
//
// Now load the driver
//
code = NtLoadDriver(&DriverPath);
if ( FAILED(code) && code != STATUS_IMAGE_ALREADY_LOADED )
{
dwarning("AUTOHIDE NONE\n"
"Failed to load 'kldbgdrv', please use local kernel debugging at least once!\n"
"Error: %s\n", winerr(code));
return code;
}
//
// Open a handle to it
//
OBJECT_ATTRIBUTES ObjectAttributes;
InitializeObjectAttributes(&ObjectAttributes, &DriverName, OBJ_CASE_INSENSITIVE, NULL, NULL);
code = NtCreateFile(&DriverHandle,
GENERIC_READ | GENERIC_WRITE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_CREATE,
FILE_NON_DIRECTORY_FILE,
NULL,
0);
if ( FAILED(code) )
{
dwarning("AUTOHIDE NONE\n"
"Failed to open 'kldbgdrv'\n"
"Error: %s\n", winerr(code));
return code;
}
}
//
// Package the input parameters into the private structure
//
KLDD_DATA_DEBUG_CONTROL KldDebugCommand;
KldDebugCommand.Command = write ? SysDbgWriteMsr : SysDbgReadMsr;
KldDebugCommand.InputBuffer = msr;
KldDebugCommand.InputBufferLength = sizeof(*msr);
//
// Send the request -- output isn't packaged, just specify directly the buffer
//
code = NtDeviceIoControlFile(DriverHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
KLDD_CODE_DEBUG_CONTROL,
&KldDebugCommand,
sizeof(KldDebugCommand),
msr,
sizeof(*msr));
if ( FAILED(code) )
{
dwarning("AUTOHIDE NONE\n"
"Failed to access model specific register, is system booted in /debug mode?\n"
"Error: %s\n", winerr(code));
return code;
}
// all ok!
return code;
}
//--------------------------------------------------------------------------
int win32_debmod_t::rdmsr(int reg, uint64 *value)
{
SYSDBG_MSR msr;
msr.reg = reg;
msr.value = 0; // shut up the compiler
NTSTATUS code;
if ( NtSystemDebugControl == NULL )
code = STATUS_NOT_IMPLEMENTED;
else
code = NtSystemDebugControl(SysDbgReadMsr, &msr, sizeof(msr), &msr, sizeof(msr), 0);
// if failed to read it with SystemDebugControl, try the driver
if ( FAILED(code) )
code = kldbgdrv_access_msr(&msr, false);
if ( SUCCEEDED(code) )
*value = msr.value;
return code;
}
//--------------------------------------------------------------------------
int win32_debmod_t::wrmsr(int reg, uint64 value)
{
SYSDBG_MSR msr;
msr.reg = reg;
msr.value = value;
NTSTATUS code;
if ( NtSystemDebugControl == NULL )
code = STATUS_NOT_IMPLEMENTED;
else
code = NtSystemDebugControl(SysDbgWriteMsr, &msr, sizeof(msr), NULL, 0, 0);
// if failed to write it with SystemDebugControl, try the driver
if ( FAILED(code) )
code = kldbgdrv_access_msr(&msr, true);
return code;
}

View File

@@ -0,0 +1,208 @@
#ifndef __NT__
#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION
#define EXCEPTION_DATATYPE_MISALIGNMENT STATUS_DATATYPE_MISALIGNMENT
#define EXCEPTION_BREAKPOINT STATUS_BREAKPOINT
#define EXCEPTION_SINGLE_STEP STATUS_SINGLE_STEP
#define EXCEPTION_ARRAY_BOUNDS_EXCEEDED STATUS_ARRAY_BOUNDS_EXCEEDED
#define EXCEPTION_FLT_DENORMAL_OPERAND STATUS_FLOAT_DENORMAL_OPERAND
#define EXCEPTION_FLT_DIVIDE_BY_ZERO STATUS_FLOAT_DIVIDE_BY_ZERO
#define EXCEPTION_FLT_INEXACT_RESULT STATUS_FLOAT_INEXACT_RESULT
#define EXCEPTION_FLT_INVALID_OPERATION STATUS_FLOAT_INVALID_OPERATION
#define EXCEPTION_FLT_OVERFLOW STATUS_FLOAT_OVERFLOW
#define EXCEPTION_FLT_STACK_CHECK STATUS_FLOAT_STACK_CHECK
#define EXCEPTION_FLT_UNDERFLOW STATUS_FLOAT_UNDERFLOW
#define EXCEPTION_INT_DIVIDE_BY_ZERO STATUS_INTEGER_DIVIDE_BY_ZERO
#define EXCEPTION_INT_OVERFLOW STATUS_INTEGER_OVERFLOW
#define EXCEPTION_PRIV_INSTRUCTION STATUS_PRIVILEGED_INSTRUCTION
#define EXCEPTION_IN_PAGE_ERROR STATUS_IN_PAGE_ERROR
#define EXCEPTION_ILLEGAL_INSTRUCTION STATUS_ILLEGAL_INSTRUCTION
#define EXCEPTION_NONCONTINUABLE_EXCEPTION STATUS_NONCONTINUABLE_EXCEPTION
#define EXCEPTION_STACK_OVERFLOW STATUS_STACK_OVERFLOW
#define EXCEPTION_INVALID_DISPOSITION STATUS_INVALID_DISPOSITION
#define EXCEPTION_GUARD_PAGE STATUS_GUARD_PAGE_VIOLATION
#define EXCEPTION_INVALID_HANDLE STATUS_INVALID_HANDLE
#define CONTROL_C_EXIT STATUS_CONTROL_C_EXIT
#define DBG_CONTROL_C 0x40010005L
#define DBG_CONTROL_BREAK 0x40010008L
#define STATUS_GUARD_PAGE_VIOLATION 0x80000001L
#define STATUS_DATATYPE_MISALIGNMENT 0x80000002L
#define STATUS_BREAKPOINT 0x80000003L
#define STATUS_SINGLE_STEP 0x80000004L
#define STATUS_ACCESS_VIOLATION 0xC0000005L
#define STATUS_IN_PAGE_ERROR 0xC0000006L
#define STATUS_INVALID_HANDLE 0xC0000008L
#define STATUS_NO_MEMORY 0xC0000017L
#define STATUS_ILLEGAL_INSTRUCTION 0xC000001DL
#define STATUS_NONCONTINUABLE_EXCEPTION 0xC0000025L
#define STATUS_INVALID_DISPOSITION 0xC0000026L
#define STATUS_ARRAY_BOUNDS_EXCEEDED 0xC000008CL
#define STATUS_FLOAT_DENORMAL_OPERAND 0xC000008DL
#define STATUS_FLOAT_DIVIDE_BY_ZERO 0xC000008EL
#define STATUS_FLOAT_INEXACT_RESULT 0xC000008FL
#define STATUS_FLOAT_INVALID_OPERATION 0xC0000090L
#define STATUS_FLOAT_OVERFLOW 0xC0000091L
#define STATUS_FLOAT_STACK_CHECK 0xC0000092L
#define STATUS_FLOAT_UNDERFLOW 0xC0000093L
#define STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000094L
#define STATUS_INTEGER_OVERFLOW 0xC0000095L
#define STATUS_PRIVILEGED_INSTRUCTION 0xC0000096L
#define STATUS_STACK_OVERFLOW 0xC00000FDL
#define STATUS_CONTROL_C_EXIT 0xC000013AL
#define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4L
#define STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5L
#define STATUS_REG_NAT_CONSUMPTION 0xC00002C9L
#define SUCCEEDED(x) (x >= 0)
#define FAILED(x) (x < 0)
#endif
#include <expr.hpp>
#include <loader.hpp>
#include "../ldr/pe/pe.h"
#include "../plugins/pdb/pdb.hpp"
#include "win32_rpc.h"
#include "dbg_rpc_hlp.h"
//--------------------------------------------------------------------------
static const char idc_win32_rdmsr_args[] = { VT_LONG, 0 };
static error_t idaapi idc_win32_rdmsr(idc_value_t *argv, idc_value_t *res)
{
uint64 value = 0; // shut up the compiler
uval_t reg = argv[0].num;
#ifdef RPC_CLIENT
void *out = NULL;
ssize_t outsize;
int code = g_dbgmod.send_ioctl(WIN32_IOCTL_RDMSR, &reg, sizeof(reg), &out, &outsize);
if ( SUCCEEDED(code) && outsize == sizeof(value) )
value = *(uint64*)out;
qfree(out);
#else
int code = g_dbgmod.rdmsr(reg, &value);
#endif
if ( FAILED(code) )
{
res->num = code;
return set_qerrno(eExecThrow); // read error, raise exception
}
res->set_int64(value);
return eOk;
}
//--------------------------------------------------------------------------
static const char idc_win32_wrmsr_args[] = { VT_LONG, VT_INT64, 0 };
static error_t idaapi idc_win32_wrmsr(idc_value_t *argv, idc_value_t *res)
{
win32_wrmsr_t msr;
msr.reg = argv[0].num;
msr.value = argv[1].i64;
#ifdef RPC_CLIENT
res->num = g_dbgmod.send_ioctl(WIN32_IOCTL_WRMSR, &msr, sizeof(msr), NULL, NULL);
#else
res->num = g_dbgmod.wrmsr(msr.reg, msr.value);
#endif
return eOk;
}
//--------------------------------------------------------------------------
// Installs or uninstalls debugger specific idc functions
static bool register_idc_funcs(bool reg)
{
static const ext_idcfunc_t idcfuncs[] =
{
{ IDC_READ_MSR, idc_win32_rdmsr, idc_win32_rdmsr_args, NULL, 0, 0 },
{ IDC_WRITE_MSR, idc_win32_wrmsr, idc_win32_wrmsr_args, NULL, 0, 0 },
};
return add_idc_funcs(idcfuncs, qnumber(idcfuncs), reg);
}
//--------------------------------------------------------------------------
void idaapi rebase_if_required_to(ea_t new_base)
{
netnode penode(PE_NODE);
ea_t currentbase = new_base;
ea_t imagebase = ea_t(penode.altval(PE_ALT_IMAGEBASE)); // loading address (usually pe.imagebase)
if ( imagebase == 0 )
{
if ( !is_miniidb() )
warning("AUTOHIDE DATABASE\n"
"IDA could not automatically determine if the program should be\n"
"rebased in the database because the database format is too old and\n"
"doesn't contain enough information.\n"
"Create a new database if you want automated rebasing to work properly.\n"
"Note you can always manually rebase the program by using the\n"
"Edit, Segments, Rebase program command.");
}
else if ( imagebase != currentbase )
{
rebase_or_warn(imagebase, currentbase);
}
}
//--------------------------------------------------------------------------
bool read_pe_header(peheader_t *pe)
{
netnode penode(PE_NODE);
return penode.valobj(pe, sizeof(peheader_t)) > 0;
}
//--------------------------------------------------------------------------
// Initialize Win32 debugger plugin
static bool win32_init_plugin(void)
{
// Remote debugger? Then nothing to initialize locally
#ifndef RPC_CLIENT
if ( !init_subsystem() )
return false;
#endif
if ( !netnode::inited() || is_miniidb() || inf_is_snapshot() )
{
#ifndef __NT__
// local debugger is available if we are running under Windows
// for other systems only the remote debugger is available
if ( !debugger.is_remote() )
return false;
#endif
}
else
{
if ( inf_get_filetype() != f_PE )
return false; // only PE files
processor_t &ph = PH;
if ( ph.id != TARGET_PROCESSOR && ph.id != -1 )
return false;
// find out the pe header
peheader_t pe;
if ( !read_pe_header(&pe) )
return false;
// debug only gui, console, or unknown applications
if ( pe.subsys != PES_WINGUI // Windows GUI
&& pe.subsys != PES_WINCHAR // Windows Character
&& pe.subsys != PES_UNKNOWN ) // Unknown
{
return false;
}
}
return true;
}
//--------------------------------------------------------------------------
inline void win32_term_plugin(void)
{
#ifndef RPC_CLIENT
term_subsystem();
#endif
}
//----------------------------------------------------------------------------
struct pdb_remote_session_t;
void close_pdb_remote_session(pdb_remote_session_t *)
{
}
#ifndef HAVE_PLUGIN_COMMENTS
//--------------------------------------------------------------------------
static const char comment[] = "Userland win32 debugger plugin";
#endif

View File

@@ -0,0 +1,203 @@
#ifndef WIN32_RPC_H
#define WIN32_RPC_H
// IOCTL codes for the win32 debugger
#define WIN32_IOCTL_RDMSR 0 // read model specific register
#define WIN32_IOCTL_WRMSR 1 // write model specific register
#define WIN32_IOCTL_READFILE 2 // server->client: read bytes from the input file
// uint64 offset;
// uint32 length;
// returns: 1 - ok
// -2 - error (text in output buffer)
// Open file for PDB retrieval.
//
// This operation will *typically* require that executable data
// be provided to the underlying MS PDB "DIA" dll. Therefore,
// there is _no_ way (currently) that this operation will
// immediately return something relevant. The client must
// poll for _OPERATION_COMPLETE-ness.
//
// client->server
// (unpacked) compiler_info_t: compiler_info
// (packed) uint64 : base_address
// (unpacked) char * : input_file
// (unpacked) char * : user symbols path
// server->client
// (packed) uint32 : session handle
#define WIN32_IOCTL_PDB_OPEN 3
// Close PDB 'session', previously opened with _PDB_OPEN.
//
// client->server
// (packed) uint32 : session handle
// server->client
// void
#define WIN32_IOCTL_PDB_CLOSE 4
// Fetch the data for one symbol.
//
// Synchronous operation.
//
// client->server
// (packed) uint32 : session handle
// (packed) uint64 : symbol ID
// server->client
// (unpacked) uint32: The integer value 1.
// (serialized) data: Packed symbol data (once).
#define WIN32_IOCTL_PDB_FETCH_SYMBOL 5
// Fetch the data for the children of a symbol.
//
// Synchronous operation.
//
// client->server
// (packed) uint32 : session handle
// (packed) uint64 : symbol ID
// (packed) uint32 : children type (a SymTagEnum)
// server->client
// (unpacked) uint32: Number of symbols whose data
// has been fetched.
// (serialized) data: Packed symbol data (N times).
#define WIN32_IOCTL_PDB_FETCH_CHILDREN 6
// Is the current operation complete?
//
// Depending on the type of the operation, the contents
// of the results will differ:
// - _OPEN
// (packed) uint64 : Global symbol ID.
// (packed) uint32 : machine type.
// (packed) uint32 : DIA version.
//
// NOTE: Currently, this IOCTL only makes sense to check
// for completeness of operation _OPEN, but this
// might change in the future.
//
// client->server
// (packed) uint32 : session handle
// server->client
// (packed) uint32 : See pdb_op_completion_t
// Depending on this first byte, the following will be come:
// pdb_op_not_complete:
// nothing
// pdb_op_complete:
// (packed) uint32 : global symbol ID
// (packed) uint32 : machine type
// (packed) uint32 : DIA version
// (unpacked) str : used file name
// pdb_op_failure:
// (unpacked) str : error message
#define WIN32_IOCTL_PDB_OPERATION_COMPLETE 7
// Get lines by VA
//
// client->server
// (packed) uint32 : session handle
// (packed) ea_t : VA
// (packed) uint64 : length
// server->client
// (packed) uint32 : the number of line-number objects
// (packed) data : the line-number objects (N times)
//
// Each of the line-number objects is transmitted like so:
// (packed) ea_t : VA
// (packed) uint32 : length
// (packed) uint32 : columnNumber
// (packed) uint32 : columnNumberEnd
// (packed) uint32 : lineNumber
// (packed) uint32 : lineNumberEnd
// (packed) uint32 : file_id
// (unpacked) byte : statement
#define WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA 8
// Get lines by coordinates
//
// client->server
// (packed) uint32 : session handle
// (packed) uint32 : file ID
// (packed) uint32 : lnnum
// (packed) uint32 : colnum
// server->client
// same as WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA
#define WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_COORDS 9
// Get symbols at EA
//
// client->server
// (packed) uint32 : session handle
// (packed) ea_t : VA
// (packed) uint64 : length
// (packed) uint32 : children type (a SymTagEnum)
// server->client
// (unpacked) uint32: Number of symbols whose data
// has been fetched.
// (serialized) data: Packed symbol data (N times).
#define WIN32_IOCTL_PDB_SIP_FETCH_SYMBOLS_AT_VA 10
// Get compilands for file
//
// client->server
// (packed) uint32 : session handle
// (packed) uint32 : file ID
// server->client
// (unpacked) uint32: Number of symbols whose data
// has been fetched.
// (serialized) data: Packed symbol data (N times).
#define WIN32_IOCTL_PDB_SIP_FETCH_FILE_COMPILANDS 11
// Get path for file ID
//
// client->server
// (packed) uint32 : session handle
// (packed) uint32 : file ID
// server->client
// (unpacked) str : the path
#define WIN32_IOCTL_PDB_SIP_FETCH_FILE_PATH 12
// Get files IDs for files corresponding to symbol
//
// client->server
// (packed) uint32 : session handle
// (packed) uint64 : symbol ID
// server->client
// (packed) uint32 : the number of IDs
// (packed) uint32 : file ID (N times).
#define WIN32_IOCTL_PDB_SIP_FETCH_SYMBOL_FILES 13
// Get files IDs for files whose name matches
//
// client->server
// (packed) uint32 : session handle
// (unpacked) str : the file name
// server->client
// (packed) uint32 : the number of IDs
// (packed) uint32 : file ID (N times).
#define WIN32_IOCTL_PDB_SIP_FIND_FILES 14
enum ioctl_pdb_code_t
{
pdb_ok = 1,
pdb_error = -2,
};
enum pdb_op_completion_t
{
pdb_op_not_complete = 0,
pdb_op_complete = 1,
pdb_op_failure = -1,
};
// WIN32_IOCTL_WRMSR uses this structure:
struct win32_wrmsr_t
{
uint32 reg;
uint64 value;
};
#endif // WIN32_RPC_H

View File

@@ -0,0 +1,406 @@
//
//
// This file contains win32 specific implementations of win32_debugger_module class
// server-side functionality only
//
//
#include <pro.h>
#include "win32_rpc.h"
#include "win32_debmod.h"
#include "dbg_rpc_hlp.h"
bool ida_export idb_utf8(qstring *, const char *, int, int) { return false; }
#include "tilfuncs.hpp"
//---------------------------------------------------------- main thread ---
static AS_PRINTF(3, 4) int pdb_ioctl_error(void **poutbuf, ssize_t *poutsize, const char *format, ...)
{
char buf[MAXSTR];
va_list va;
va_start(va, format);
int len = qvsnprintf(buf, sizeof(buf), format, va);
va_end(va);
msg("%s", buf);
*poutsize = len + 1;
*poutbuf = qstrdup(buf);
return pdb_error;
}
//---------------------------------------------------------- main thread ---
void win32_debmod_t::handle_pdb_thread_request(void *_pdb_rsess)
{
pdb_remote_session_t *pdb_rsess = (pdb_remote_session_t *) _pdb_rsess;
pdb_remote_session_t::client_read_request_t &rr = pdb_rsess->client_read_request;
if ( rr.kind == READ_INPUT_FILE )
{
// read input file
bytevec_t req;
req.pack_dq(rr.off_ea);
req.pack_dd(rr.size);
void *outbuf = NULL;
ssize_t outsize = 0;
// send request to IDA
int rc = send_ioctl(WIN32_IOCTL_READFILE, req.begin(), req.size(), &outbuf, &outsize);
if ( rc == 1 && outbuf != NULL )
{
// OK
size_t copylen = qmin(rr.size, outsize);
memcpy(rr.buffer, outbuf, copylen);
rr.size = copylen;
rr.result = true;
}
else
{
rr.result = false;
}
if ( outbuf != NULL )
qfree(outbuf);
}
else if ( rr.kind == READ_MEMORY )
{
// read memory
ea_t ea = ea_t(rr.off_ea);
void *buf = rr.buffer;
size_t size = rr.size;
ssize_t rc = _read_memory(ea, buf, size);
if ( rc >= 0 )
rr.size = rc;
rr.result = rc >= 0;
}
else
{
// unknown request
rr.result = false;
}
rr.read_complete();
}
//-------------------------------------------------------------------------
pdb_remote_session_t *win32_debmod_t::get_pdb_session(int id)
{
for ( size_t i = 0; i < pdb_remote_sessions.size(); ++i )
if ( pdb_remote_sessions[i]->get_id() == id )
return pdb_remote_sessions[i];
return NULL;
}
//-------------------------------------------------------------------------
void win32_debmod_t::delete_pdb_session(int id)
{
for ( size_t i = 0; i < pdb_remote_sessions.size(); ++i )
{
if ( pdb_remote_sessions[i]->get_id() == id )
{
pdb_remote_sessions[i]->stop();
delete pdb_remote_sessions[i];
pdb_remote_sessions.erase(pdb_remote_sessions.begin() + i);
break;
}
}
}
//----------------------------------------------------------------------------
void close_pdb_remote_session(pdb_remote_session_t *session)
{
session->stop();
delete session;
}
//---------------------------------------------------------- main thread ---
int idaapi win32_debmod_t::handle_ioctl(
int fn,
const void *buf,
size_t size,
void **poutbuf,
ssize_t *poutsize)
{
qnotused(size);
int sid = 0; // pdb_remote_session_t ID
pdb_remote_session_t *pdb_rsess = NULL;
switch ( fn )
{
case WIN32_IOCTL_RDMSR:
QASSERT(30119, size == sizeof(uval_t));
{
uint64 value;
uval_t reg = *(uval_t *)buf;
int code = rdmsr(reg, &value);
if ( SUCCEEDED(code) )
{
*poutbuf = qalloc(sizeof(value));
if ( *poutbuf != NULL )
{
memcpy(*poutbuf, &value, sizeof(value));
*poutsize = sizeof(value);
}
}
return code;
}
case WIN32_IOCTL_WRMSR:
QASSERT(30120, size == sizeof(win32_wrmsr_t));
{
win32_wrmsr_t &msr = *(win32_wrmsr_t *)buf;
return wrmsr(msr.reg, msr.value);
}
#define ENSURE_PDB_THREAD() \
do \
{ \
if ( !pdb_thread.is_running() ) \
return pdb_ioctl_error( \
poutbuf, poutsize, "PDB thread not running?!?\n"); \
} while ( false )
#define GET_OPENED_SESSION() \
do \
{ \
if ( mmdsr.empty() ) \
return pdb_error; \
sid = mmdsr.unpack_dd(); \
pdb_rsess = get_pdb_session(sid); \
} while ( false )
#define ENSURE_SESSION_OPENED() \
do \
{ \
GET_OPENED_SESSION(); \
if ( pdb_rsess == NULL ) \
return pdb_ioctl_error( \
poutbuf, poutsize, "Unknown PDB session #%d\n", sid); \
} while ( false )
#define FLUSH_PDB_REQUEST_STORAGE() \
do \
{ \
size_t sz = pdb_rsess->storage.size(); \
uint8 *raw = (uint8 *) qalloc(sz); \
if ( raw == NULL ) \
return pdb_error; \
memcpy(raw, pdb_rsess->storage.begin(), sz); \
*poutbuf = raw; \
*poutsize = sz; \
} while ( false )
case WIN32_IOCTL_PDB_OPEN:
{
pdb_thread.start_if_needed();
memory_deserializer_t mmdsr(buf, size);
pdb_rsess = new pdb_remote_session_t();
pdb_remote_sessions.push_back(pdb_rsess);
compiler_info_t cci;
mmdsr.unpack_obj(&cci, sizeof(cci));
pdbargs_t args;
args.pdb_path = mmdsr.unpack_str();
args.input_path = mmdsr.unpack_str();
mmdsr.unpack_obj(&args.pdb_sign, sizeof(args.pdb_sign));
args.spath = mmdsr.unpack_str();
args.loaded_base = mmdsr.unpack_ea64();
args.flags = mmdsr.unpack_dd();
pdb_rsess->open(cci, args);
bytevec_t storage;
storage.pack_dd(pdb_rsess->get_id());
*poutsize = storage.size();
*poutbuf = storage.extract();
pdb_rsess->is_opening = true;
}
return pdb_ok;
case WIN32_IOCTL_PDB_OPERATION_COMPLETE:
// This is used, in a polling fashion, by the the client,
// to check on completeness of the fetch. At the same time,
// this is our wake-up call for looking whether the
// fetch thread requires more information.
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
// If we've got a read request, handle it now
if ( pdb_rsess->client_read_request.pending() )
handle_pdb_thread_request(pdb_rsess);
bytevec_t storage;
bool done = pdb_rsess->is_done();
if ( done )
{
pdb_rsess->is_opening = false;
const char *fname = pdb_rsess->session_ref.session->get_used_fname();
local_pdb_access_t *acc = pdb_rsess->session_ref.session->pdb_access;
HRESULT hr = acc != NULL ? S_OK : E_FAIL;
if ( SUCCEEDED(hr) )
{
storage.pack_dd(uint32(pdb_op_complete));
storage.pack_dd(acc->get_global_symbol_id());
storage.pack_dd(acc->get_machine_type());
storage.pack_dd(acc->get_dia_version());
storage.pack_str(fname);
}
else
{
storage.pack_dd(uint32(pdb_op_failure));
qstring errmsg;
errmsg.sprnt("%s: %s\n", fname, pdberr(hr));
storage.pack_str(errmsg.c_str());
delete_pdb_session(sid);
}
}
else
{
storage.pack_dd(uint32(pdb_op_not_complete));
}
*poutsize = storage.size();
*poutbuf = storage.extract();
return pdb_ok;
}
case WIN32_IOCTL_PDB_FETCH_SYMBOL:
case WIN32_IOCTL_PDB_FETCH_CHILDREN:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
DWORD sym_id = mmdsr.unpack_dd();
// msg("Fetch%s 0x%x\n",
// (fn == WIN32_IOCTL_PDB_FETCH_CHILDREN ? " children for" : ""),
// (uint32) sym);
bool ok;
if ( fn == WIN32_IOCTL_PDB_FETCH_SYMBOL )
{
// Symbol
ok = pdb_rsess->fetch_symbol(sym_id);
}
else
{
// Children
enum SymTagEnum children_type = (enum SymTagEnum) mmdsr.unpack_dd();
ok = pdb_rsess->fetch_children(sym_id, children_type);
}
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_CLOSE:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
GET_OPENED_SESSION();
if ( pdb_rsess != NULL )
delete_pdb_session(sid);
return pdb_ok;
}
case WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
ea_t va = mmdsr.unpack_ea64();
uint64 length = mmdsr.unpack_dq();
bool ok = pdb_rsess->fetch_lines_by_va(
va, length);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_COORDS:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
DWORD file_id = mmdsr.unpack_dd();
DWORD lnnum = mmdsr.unpack_dd();
DWORD colnum = mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_lines_by_coords(file_id, lnnum, colnum);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_SYMBOLS_AT_VA:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
ea_t va = mmdsr.unpack_ea64();
uint64 length = mmdsr.unpack_dq();
enum SymTagEnum type = (enum SymTagEnum) mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_symbols_at_va(va, length, type);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_FILE_COMPILANDS:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
uint32 file_id = mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_file_compilands(file_id);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_FILE_PATH:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
uint32 file_id = mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_file_path(file_id);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FETCH_SYMBOL_FILES:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
DWORD sym_id = mmdsr.unpack_dd();
bool ok = pdb_rsess->fetch_symbol_files(sym_id);
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
case WIN32_IOCTL_PDB_SIP_FIND_FILES:
{
ENSURE_PDB_THREAD();
memory_deserializer_t mmdsr(buf, size);
ENSURE_SESSION_OPENED();
qstring fname = mmdsr.unpack_str();
bool ok = pdb_rsess->fetch_files(fname.c_str());
if ( ok )
FLUSH_PDB_REQUEST_STORAGE();
return ok ? pdb_ok : pdb_error;
}
default:
break;
}
return 0;
}

View File

@@ -0,0 +1,15 @@
//
//
// This file contains win32 specific implementations of win32_debugger_module class
// IDA-side functionality only (for local debugger)
//
//
#include <pro.h>
#include "win32_debmod.h"
//--------------------------------------------------------------------------
int idaapi win32_debmod_t::handle_ioctl(int /*fn*/, const void * /*buf*/, size_t /*size*/, void ** /*poutbuf*/, ssize_t * /*poutsize*/)
{
return 0;
}

View File

@@ -0,0 +1,128 @@
#define REMOTE_DEBUGGER
#define RPC_CLIENT
static const char wanted_name[] = "Remote Windows debugger";
#define DEBUGGER_NAME "win32"
#define PROCESSOR_NAME "metapc"
#define DEFAULT_PLATFORM_NAME "win32"
#define TARGET_PROCESSOR PLFM_386
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_WIN32_USER
#define DEBUGGER_FLAGS (DBG_FLAG_REMOTE \
| DBG_FLAG_EXITSHOTOK \
| DBG_FLAG_LOWCNDS \
| DBG_FLAG_DEBTHREAD \
| DBG_FLAG_ANYSIZE_HWBPT)
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
#define HAVE_APPCALL
#define S_FILETYPE f_PE
#define win32_term_plugin term_plugin
#include <pro.h>
#include <idp.hpp>
#include <idd.hpp>
#include <ua.hpp>
#include <range.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <network.hpp>
#include "w32sehch.h"
#include "dbg_rpc_client.h"
#include "rpc_debmod.h"
class win32_rpc_debmod_t : public rpc_debmod_t
{
typedef rpc_debmod_t inherited;
public:
win32_rpc_debmod_t(const char *default_platform)
: rpc_debmod_t(default_platform) {}
virtual bool idaapi open_remote(
const char *hostname,
int port_number,
const char *password,
qstring *errbuf) override
{
char path[QMAXPATH];
get_input_file_path(path, sizeof(path));
pdb_file_path = path;
return inherited::open_remote(hostname, port_number, password, errbuf);
}
qstring pdb_file_path;
};
win32_rpc_debmod_t g_dbgmod(DEFAULT_PLATFORM_NAME);
#include "common_stub_impl.cpp"
#include "pc_local_impl.cpp"
#include "win32_local_impl.cpp"
//--------------------------------------------------------------------------
// handler on IDA: Server -> IDA
static int ioctl_handler(
rpc_engine_t * /*rpc*/,
int fn,
const void *buf,
size_t size,
void **poutbuf,
ssize_t *poutsize)
{
qnotused(size);
switch ( fn )
{
case WIN32_IOCTL_READFILE:
{
user_cancelled();
const uchar *ptr = (const uchar *)buf;
const uchar *end = ptr + size;
uint64 offset = unpack_dq(&ptr, end);
uint32 length = unpack_dd(&ptr, end);
*poutbuf = NULL;
*poutsize = 0;
if ( length != 0 )
{
FILE *infile = qfopen(g_dbgmod.pdb_file_path.c_str(), "rb");
if ( infile == NULL )
return -2;
void *outbuf = qalloc(length);
if ( outbuf == NULL )
return -2;
qfseek(infile, offset, SEEK_SET);
int readlen = qfread(infile, outbuf, length);
qfclose(infile);
if ( readlen < 0 || readlen > length )
{
qfree(outbuf);
return -2;
}
*poutbuf = outbuf;
*poutsize = readlen;
}
return 1;
}
}
return 0;
}
//lint -esym(528,init_plugin) static symbol '' not referenced
//--------------------------------------------------------------------------
// Initialize Win32 debugger stub
static bool init_plugin(void)
{
// There is no need to call win32_init_plugin() (which checks the PE
// file parameters) if the debugger is only being used to fetch PDBs.
bool should_init = !netnode(PDB_NODE_NAME).altval(PDB_LOADING_WIN32_DBG);
if ( should_init && !win32_init_plugin() )
return false;
g_dbgmod.set_ioctl_handler(ioctl_handler);
return true;
}
#include "common_local_impl.cpp"

View File

@@ -0,0 +1,282 @@
#ifndef WIN32_UNDOC_H
#define WIN32_UNDOC_H
// Definitions for Windows DDK and other undocumented MS Windows types.
// Instead of requiring a DDK and NDK, we just use this file.
//---------------------------------------------------------------------------
// WinDDK types
struct UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWCH Buffer;
};
typedef UNICODE_STRING *PUNICODE_STRING;
extern "C" char _RTL_CONSTANT_STRING_type_check(const WCHAR *s);
// __typeof would be desirable here instead of sizeof.
template <size_t N> class _RTL_CONSTANT_STRING_remove_const_template_class;
template <> class _RTL_CONSTANT_STRING_remove_const_template_class<sizeof(char)>
{
public:
typedef char T;
};
template <> class _RTL_CONSTANT_STRING_remove_const_template_class<sizeof(WCHAR)>
{
public:
typedef WCHAR T;
};
#define _RTL_CONSTANT_STRING_remove_const_macro(s) \
(const_cast<_RTL_CONSTANT_STRING_remove_const_template_class<sizeof((s)[0])>::T*>(s))
#define RTL_CONSTANT_STRING(s) \
{ \
sizeof(s) - sizeof((s)[0]), \
sizeof(s) / sizeof(_RTL_CONSTANT_STRING_type_check(s)), \
_RTL_CONSTANT_STRING_remove_const_macro(s) \
}
typedef struct _OBJECT_ATTRIBUTES64
{
ULONG Length;
ULONG64 RootDirectory;
ULONG64 ObjectName;
ULONG Attributes;
ULONG64 SecurityDescriptor;
ULONG64 SecurityQualityOfService;
} OBJECT_ATTRIBUTES64;
typedef OBJECT_ATTRIBUTES64 *POBJECT_ATTRIBUTES64;
typedef CONST OBJECT_ATTRIBUTES64 *PCOBJECT_ATTRIBUTES64;
typedef struct _OBJECT_ATTRIBUTES32
{
ULONG Length;
ULONG RootDirectory;
ULONG ObjectName;
ULONG Attributes;
ULONG SecurityDescriptor;
ULONG SecurityQualityOfService;
} OBJECT_ATTRIBUTES32;
typedef OBJECT_ATTRIBUTES32 *POBJECT_ATTRIBUTES32;
typedef CONST OBJECT_ATTRIBUTES32 *PCOBJECT_ATTRIBUTES32;
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;
#define InitializeObjectAttributes(p,n,a,r,s) \
{ \
(p)->Length = sizeof(OBJECT_ATTRIBUTES); \
(p)->RootDirectory = (r); \
(p)->Attributes = (a); \
(p)->ObjectName = (n); \
(p)->SecurityDescriptor = (s); \
(p)->SecurityQualityOfService = NULL; \
}
//
// Definitions for Object Creation
//
#define OBJ_INHERIT 0x00000002L
#define OBJ_PERMANENT 0x00000010L
#define OBJ_EXCLUSIVE 0x00000020L
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define OBJ_OPENIF 0x00000080L
#define OBJ_OPENLINK 0x00000100L
#define OBJ_KERNEL_HANDLE 0x00000200L
#define OBJ_FORCE_ACCESS_CHECK 0x00000400L
#define OBJ_VALID_ATTRIBUTES 0x000007F2L
//---------------------------------------------------------------------------
// Undocumented types - pulled out of MS Windows NDK by Alex Ionecsu
//
// Privilege constants
//
#define SE_MIN_WELL_KNOWN_PRIVILEGE (2L)
#define SE_CREATE_TOKEN_PRIVILEGE (2L)
#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L)
#define SE_LOCK_MEMORY_PRIVILEGE (4L)
#define SE_INCREASE_QUOTA_PRIVILEGE (5L)
#define SE_UNSOLICITED_INPUT_PRIVILEGE (6L)
#define SE_MACHINE_ACCOUNT_PRIVILEGE (6L)
#define SE_TCB_PRIVILEGE (7L)
#define SE_SECURITY_PRIVILEGE (8L)
#define SE_TAKE_OWNERSHIP_PRIVILEGE (9L)
#define SE_LOAD_DRIVER_PRIVILEGE (10L)
#define SE_SYSTEM_PROFILE_PRIVILEGE (11L)
#define SE_SYSTEMTIME_PRIVILEGE (12L)
#define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L)
#define SE_INC_BASE_PRIORITY_PRIVILEGE (14L)
#define SE_CREATE_PAGEFILE_PRIVILEGE (15L)
#define SE_CREATE_PERMANENT_PRIVILEGE (16L)
#define SE_BACKUP_PRIVILEGE (17L)
#define SE_RESTORE_PRIVILEGE (18L)
#define SE_SHUTDOWN_PRIVILEGE (19L)
#define SE_DEBUG_PRIVILEGE (20L)
#define SE_AUDIT_PRIVILEGE (21L)
#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L)
#define SE_CHANGE_NOTIFY_PRIVILEGE (23L)
#define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L)
#define SE_MAX_WELL_KNOWN_PRIVILEGE (SE_REMOTE_SHUTDOWN_PRIVILEGE)
// Undocumented NTSystemDebugControl function (to read/write MSRs)
enum SYSDBG_COMMAND
{
SysDbgQueryModuleInformation = 1,
SysDbgQueryTraceInformation = 2,
SysDbgSetTracepoint = 3,
SysDbgSetSpecialCall = 4,
SysDbgClearSpecialCalls = 5,
SysDbgQuerySpecialCalls = 6,
SysDbgReadMsr = 16,
SysDbgWriteMsr = 17,
};
struct SYSDBG_MSR
{
uint32 reg;
uint32 padding;
uint64 value;
};
#define NTSTATUS int
#ifndef NTAPI
#define NTAPI WINAPI
#endif
#ifndef STATUS_NOT_IMPLEMENTED
#define STATUS_NOT_IMPLEMENTED 0xC0000002
#endif
#ifndef STATUS_IMAGE_ALREADY_LOADED
#define STATUS_IMAGE_ALREADY_LOADED 0xC000010E
#endif
//
// NtCreateFile OpenType Flags
//
#define FILE_SUPERSEDE 0x00000000
#define FILE_OPEN 0x00000001
#define FILE_CREATE 0x00000002
#define FILE_OPEN_IF 0x00000003
#define FILE_OVERWRITE 0x00000004
#define FILE_OVERWRITE_IF 0x00000005
#define FILE_MAXIMUM_DISPOSITION 0x00000005
//
// NtCreateFile Flags
//
#define FILE_DIRECTORY_FILE 0x00000001
#define FILE_WRITE_THROUGH 0x00000002
#define FILE_SEQUENTIAL_ONLY 0x00000004
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_CREATE_TREE_CONNECTION 0x00000080
#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
#define FILE_NO_EA_KNOWLEDGE 0x00000200
#define FILE_OPEN_FOR_RECOVERY 0x00000400
#define FILE_RANDOM_ACCESS 0x00000800
#define FILE_DELETE_ON_CLOSE 0x00001000
#define FILE_OPEN_BY_FILE_ID 0x00002000
#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
#define FILE_NO_COMPRESSION 0x00008000
#define FILE_RESERVE_OPFILTER 0x00100000
#define FILE_OPEN_REPARSE_POINT 0x00200000
#define FILE_OPEN_NO_RECALL 0x00400000
#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
//
// Interface for communicating with private WinDBG interface
//
#define KLDD_CODE_DEBUG_CONTROL \
CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
typedef struct _KLDD_DATA_DEBUG_CONTROL
{
SYSDBG_COMMAND Command;
PVOID InputBuffer;
SIZE_T InputBufferLength;
} KLDD_DATA_DEBUG_CONTROL, *PKLDD_DATA_DEBUG_CONTROL;
//
// I/O Status Block
//
typedef struct _IO_STATUS_BLOCK
{
union
{
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
//
// APC Callback for NtDeviceIoControlFile
//
typedef VOID
(NTAPI *PIO_APC_ROUTINE)(
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved);
typedef NTSTATUS NTAPI NtSystemDebugControl_t(
IN SYSDBG_COMMAND Command,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength,
OUT PULONG ReturnLength OPTIONAL);
typedef NTSTATUS NTAPI NtLoadDriver_t(
IN PUNICODE_STRING DriverServiceName);
typedef NTSTATUS NTAPI NtUnloadDriver_t(
IN PUNICODE_STRING DriverServiceName);
typedef NTSTATUS NTAPI RtlAdjustPrivilege_t(
IN ULONG Privilege,
IN BOOLEAN NewValue,
IN BOOLEAN ForThread,
OUT PBOOLEAN OldValue);
typedef NTSTATUS NTAPI NtCreateFile_t(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength);
typedef NTSTATUS NTAPI NtDeviceIoControlFile_t(
IN HANDLE DeviceHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
IN PVOID UserApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG IoControlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferSize,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferSize);
#endif // define WIN32_UNDOC_H

View File

@@ -0,0 +1,46 @@
/*
This is main source code for the local win32 debugger module
*/
static const char wanted_name[] = "Local Windows debugger";
#define DEBUGGER_NAME "win32"
#define PROCESSOR_NAME "metapc"
#define TARGET_PROCESSOR PLFM_386
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_WIN32_USER
#define DEBUGGER_FLAGS (DBG_FLAG_EXITSHOTOK \
| DBG_FLAG_LOWCNDS \
| DBG_FLAG_DEBTHREAD \
| DBG_FLAG_ANYSIZE_HWBPT)
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
#define HAVE_APPCALL
#define S_FILETYPE f_PE
// We must rename those method because common files
// refer to them as init_plugin/term_plugin
// Some other debugger modules compatible with win32
// have their own init/term and still call win32_init/term
// (since no renaming takes place)
#define win32_init_plugin init_plugin
#define win32_term_plugin term_plugin
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <objidl.h>
#include <fpro.h>
#include <ua.hpp>
#include <idd.hpp>
#include <loader.hpp>
#include "win32_debmod.h"
#include "w32sehch.h"
win32_debmod_t g_dbgmod;
#include "common_stub_impl.cpp"
#include "pc_local_impl.cpp"
#include "win32_local_impl.cpp"
#include "common_local_impl.cpp"
#include "win32_server_stub.cpp"

View File

@@ -0,0 +1,87 @@
#include <windows.h>
#include <pro.h>
#include "winbase_debmod.h"
#include "win32_util.hpp"
#ifndef __X86__
typedef BOOL WINAPI IsWow64Process_t(HANDLE, PBOOL);
static IsWow64Process_t *_IsWow64Process = NULL;
#endif
//--------------------------------------------------------------------------
//lint -esym(818,handle) could be pointer to const
wow64_state_t check_wow64_handle(HANDLE handle)
{
#ifdef __X86__
qnotused(handle);
#else
if ( _IsWow64Process == NULL )
{
HMODULE k32 = GetModuleHandle(kernel32_dll);
*(FARPROC*)&_IsWow64Process = GetProcAddress(k32, TEXT("IsWow64Process"));
if ( _IsWow64Process == NULL )
return WOW64_NONE; // unknown
}
BOOL bIsWow64 = FALSE;
if ( _IsWow64Process(handle, &bIsWow64) && bIsWow64 != 0 )
return WOW64_YES;
#endif
return WOW64_NO;
}
//--------------------------------------------------------------------------
wow64_state_t check_wow64_pid(int pid)
{
wow64_state_t r = WOW64_BAD; // assume pid is bad
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if ( h != NULL )
{
r = check_wow64_handle(h);
CloseHandle(h);
}
return r;
}
//--------------------------------------------------------------------------
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4996) // GetVersionEx was declared deprecated
#endif
win_version_t::win_version_t()
{
OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVersionInfo);
ver_ok = GetVersionEx(&OSVersionInfo) != 0; //lint !e2586 is deprecated
#ifdef __X86__
is_64bit_os = false;
if ( OSVersionInfo.dwMajorVersion > 5
|| OSVersionInfo.dwMajorVersion == 5 && OSVersionInfo.dwMinorVersion >= 1 )
{
is_64bit_os = check_wow64_handle(GetCurrentProcess()) > 0;
}
#endif
}
#ifdef _MSC_VER
# pragma warning(pop)
#endif
//--------------------------------------------------------------------------
win_tool_help_t::win_tool_help_t()
{
use_debug_break = qgetenv("IDA_DEBUGBREAKPROCESS");
HMODULE kern_handle = GetModuleHandle(kernel32_dll);
*(FARPROC*)&_DebugActiveProcessStop = GetProcAddress(kern_handle, TEXT("DebugActiveProcessStop"));
*(FARPROC*)&_DebugBreakProcess = GetProcAddress(kern_handle, TEXT("DebugBreakProcess"));
// find the needed functions
*(FARPROC*)&_CreateToolhelp32Snapshot = GetProcAddress(kern_handle, TEXT("CreateToolhelp32Snapshot"));
*(FARPROC*)&_Process32First = GetProcAddress(kern_handle, TEXT("Process32First"));
*(FARPROC*)&_Process32Next = GetProcAddress(kern_handle, TEXT("Process32Next"));
*(FARPROC*)&_Module32First = GetProcAddress(kern_handle, TEXT("Module32First"));
*(FARPROC*)&_Module32Next = GetProcAddress(kern_handle, TEXT("Module32Next"));
inited = _CreateToolhelp32Snapshot != NULL
&& _Process32First != NULL
&& _Process32Next != NULL
&& _Module32First != NULL
&& _Module32Next != NULL;
}

View File

@@ -0,0 +1,347 @@
//
// Wrapper for Windows ToolHelp library: enumerate processes/modules
//
// PSAPI.DLL: NT, 2K, XP/2K3
// KERNEL32.DLL (ToolHelp functions): 9X, ME, 2K, XP/2K3
//? add NT support
#ifndef __TOOLHELP_HPP__
#define __TOOLHELP_HPP__
#ifdef __NT__
#include <windows.h>
#include <Tlhelp32.h>
#include <dbghelp.h>
#include <segment.hpp>
#ifdef UNICODE
#define LookupPrivilegeValue_Name "LookupPrivilegeValueW"
#else
#define LookupPrivilegeValue_Name "LookupPrivilegeValueA"
#endif
#ifndef TH32CS_SNAPNOHEAPS
# define TH32CS_SNAPNOHEAPS 0x0
#endif
//--------------------------------------------------------------------------
enum wow64_state_t
{
WOW64_NONE = -2, // unknown yet
WOW64_BAD = -1,
WOW64_NO = 0,
WOW64_YES = 1,
};
wow64_state_t check_wow64_handle(HANDLE handle);
wow64_state_t check_wow64_pid(int pid);
//--------------------------------------------------------------------------
class win_version_t
{
public:
win_version_t();
const OSVERSIONINFO &get_info() const { return OSVersionInfo; }
bool ok() { return ver_ok; }
bool is_NT()
{
return ok() && OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT;
}
bool is_strictly_xp() // Is strictly XP (32bit)?
{
return ok()
&& is_NT()
&& OSVersionInfo.dwMajorVersion == 5
&& OSVersionInfo.dwMinorVersion == 1;
}
bool is_DW32()
{
return ok() && OSVersionInfo.dwPlatformId == 3;
}
bool is_2K() // Is at least Win2K?
{
return ok() && OSVersionInfo.dwMajorVersion >= 5;
}
bool is_64bitOS()
{
#ifndef __X86__
return true;
#else
return is_64bit_os;
#endif
}
//--------------------------------------------------------------------------
// GetProcessDEPPolicy() is broken for Win8, Win8.1, Win10:
// always set *lpPermanent == CL register, if not permanently policy.
// https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/05683d76-3c8a-49de-91e3-9d7ab8492f39/getprocessdeppolicy-does-not-set-the-correct-value-to-lppermanent?forum=windowscompatibility
bool is_GetProcessDEPPolicy_broken()
{
return ok()
&& ((OSVersionInfo.dwMajorVersion == 10 && OSVersionInfo.dwMinorVersion == 0) // Windows 10
|| (OSVersionInfo.dwMajorVersion == 6 && OSVersionInfo.dwMinorVersion == 3) // Windows 8.1
|| (OSVersionInfo.dwMajorVersion == 6 && OSVersionInfo.dwMinorVersion == 2)); // Windows 8
}
private:
OSVERSIONINFO OSVersionInfo;
bool ver_ok;
#ifdef __X86__
bool is_64bit_os;
#endif
};
//--------------------------------------------------------------------------
//-V:win_tool_help_t:730 Not all members of a class are initialized inside the constructor
class win_tool_help_t
{
public:
win_tool_help_t();
bool ok() { return inited; }
bool use_debug_break_process();
bool debug_break_process(HANDLE process_handle);
bool use_debug_detach_process();
bool debug_detach_process(pid_t pid);
private:
// function prototypes
typedef HANDLE WINAPI CreateToolhelp32Snapshot_t(DWORD dwFlags, DWORD th32ProcessID);
typedef BOOL WINAPI Process32First_t(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
typedef BOOL WINAPI Process32Next_t(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
typedef BOOL WINAPI Module32First_t(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef BOOL WINAPI Module32Next_t(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
typedef BOOL WINAPI DebugActiveProcessStop_t(DWORD dwProcessID);
typedef BOOL WINAPI DebugBreakProcess_t(HANDLE Process);
typedef BOOL WINAPI CloseToolhelp32Snapshot_t(HANDLE hSnapshot);
// functions pointers
CreateToolhelp32Snapshot_t *_CreateToolhelp32Snapshot;
Process32First_t *_Process32First;
Process32Next_t *_Process32Next;
Module32First_t *_Module32First;
Module32Next_t *_Module32Next;
DebugActiveProcessStop_t *_DebugActiveProcessStop;
DebugBreakProcess_t *_DebugBreakProcess;
bool inited;
bool use_debug_break;
friend class toolhelp_snapshot_t;
friend class process_snapshot_t;
friend class module_snapshot_t;
};
//--------------------------------------------------------------------------
class toolhelp_snapshot_t
{
public:
inline toolhelp_snapshot_t(win_tool_help_t *tool);
inline ~toolhelp_snapshot_t();
inline bool ok();
inline bool open(uint32 flags, pid_t pid);
inline void close();
inline uint32 last_err();
protected:
bool seterr(); // always returns 'false' for convenience
win_tool_help_t *t;
HANDLE h;
uint32 last_error;
};
//--------------------------------------------------------------------------
class process_snapshot_t: public toolhelp_snapshot_t
{
public:
inline process_snapshot_t(win_tool_help_t *tool);
inline bool first(uint32 flags, LPPROCESSENTRY32 lppe);
inline bool next(LPPROCESSENTRY32 lppe);
};
//--------------------------------------------------------------------------
class module_snapshot_t: public toolhelp_snapshot_t
{
public:
inline module_snapshot_t(win_tool_help_t *tool);
inline bool first(uint32 flags, pid_t pid, LPMODULEENTRY32 lpme);
inline bool next(LPMODULEENTRY32 lpme);
};
//--------------------------------------------------------------------------
inline bool win_tool_help_t::use_debug_break_process()
{
return use_debug_break && _DebugBreakProcess != NULL;
}
//--------------------------------------------------------------------------
inline bool win_tool_help_t::debug_break_process(HANDLE process_handle)
{
return process_handle != INVALID_HANDLE_VALUE && _DebugBreakProcess(process_handle);
}
//--------------------------------------------------------------------------
inline bool win_tool_help_t::use_debug_detach_process()
{
return _DebugActiveProcessStop != NULL;
}
//--------------------------------------------------------------------------
inline bool win_tool_help_t::debug_detach_process(pid_t pid)
{
return _DebugActiveProcessStop != NULL && _DebugActiveProcessStop(pid);
}
//--------------------------------------------------------------------------
inline process_snapshot_t::process_snapshot_t(win_tool_help_t *tool)
: toolhelp_snapshot_t(tool)
{
}
//--------------------------------------------------------------------------
inline bool process_snapshot_t::first(uint32 flags, LPPROCESSENTRY32 lppe)
{
open(TH32CS_SNAPPROCESS | flags, 0);
lppe->dwSize = sizeof(PROCESSENTRY32);
if ( ok() && t->_Process32First(h, lppe) )
{
// ignore "System Process" (ID==0)
return lppe->th32ProcessID != 0 || next(lppe);
}
return seterr();
}
//--------------------------------------------------------------------------
inline bool process_snapshot_t::next(LPPROCESSENTRY32 lppe)
{
while ( ok() )
{
if ( !t->_Process32Next(h, lppe) )
break;
// ignore "System Process" (ID==0)
if ( lppe->th32ProcessID != 0 )
return true;
}
return seterr();
}
//--------------------------------------------------------------------------
inline module_snapshot_t::module_snapshot_t(win_tool_help_t *tool)
: toolhelp_snapshot_t(tool)
{
}
//--------------------------------------------------------------------------
inline bool module_snapshot_t::first(uint32 flags, pid_t pid, LPMODULEENTRY32 lpme)
{
wow64_state_t state = check_wow64_pid(pid);
flags |= state == WOW64_YES ? TH32CS_SNAPMODULE32 : 0;
if ( !open(TH32CS_SNAPMODULE | flags, pid) )
return false;
lpme->dwSize = sizeof(MODULEENTRY32);
if ( t->_Module32First(h, lpme) )
return true;
return seterr();
}
//--------------------------------------------------------------------------
inline bool module_snapshot_t::next(LPMODULEENTRY32 lpme)
{
if ( ok() )
return false;
if ( t->_Module32Next(h, lpme) )
return true;
seterr();
return false;
}
//--------------------------------------------------------------------------
inline toolhelp_snapshot_t::toolhelp_snapshot_t(win_tool_help_t *tool)
: t(tool), h(INVALID_HANDLE_VALUE), last_error(0)
{
}
//--------------------------------------------------------------------------
inline toolhelp_snapshot_t::~toolhelp_snapshot_t()
{
close();
}
//--------------------------------------------------------------------------
inline bool toolhelp_snapshot_t::ok()
{
return h != INVALID_HANDLE_VALUE;
}
//--------------------------------------------------------------------------
// // always returns 'false' for convenience
inline bool toolhelp_snapshot_t::seterr()
{
last_error = GetLastError();
return false;
}
//--------------------------------------------------------------------------
// // always returns 'false' for convenience
inline uint32 toolhelp_snapshot_t::last_err()
{
return last_error;
}
//--------------------------------------------------------------------------
inline bool toolhelp_snapshot_t::open(uint32 flags, pid_t pid)
{
if ( !t->ok() )
return false;
close();
for ( int cnt=0; cnt < 5; cnt++ )
{
h = t->_CreateToolhelp32Snapshot(flags, pid);
if ( h != INVALID_HANDLE_VALUE )
return true;
seterr();
// MSDN: If the function fails with ERROR_BAD_LENGTH, retry
// the function until it succeeds.
if ( last_err() != ERROR_BAD_LENGTH
|| (flags & (TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32)) == 0 )
{
break;
}
}
return false;
}
//--------------------------------------------------------------------------
inline void toolhelp_snapshot_t::close()
{
if ( t->ok() && h != INVALID_HANDLE_VALUE )
CloseHandle(h);
}
//--------------------------------------------------------------------------
// convert Windows protection modes to IDA protection modes
inline uchar win_prot_to_ida_perm(DWORD protection)
{
uchar perm = 0;
if ( protection & PAGE_READONLY )
perm |= SEGPERM_READ;
if ( protection & PAGE_READWRITE )
perm |= SEGPERM_READ | SEGPERM_WRITE;
if ( protection & PAGE_WRITECOPY )
perm |= SEGPERM_READ | SEGPERM_WRITE;
if ( protection & PAGE_EXECUTE )
perm |= SEGPERM_EXEC;
if ( protection & PAGE_EXECUTE_READ )
perm |= SEGPERM_READ | SEGPERM_EXEC;
if ( protection & PAGE_EXECUTE_READWRITE )
perm |= SEGPERM_READ | SEGPERM_WRITE | SEGPERM_EXEC;
if ( protection & PAGE_EXECUTE_WRITECOPY )
perm |= SEGPERM_READ | SEGPERM_WRITE | SEGPERM_EXEC;
return perm;
}
#endif // __NT__
#endif // __TOOLHELP_HPP__

View File

@@ -0,0 +1,633 @@
#include <windows.h>
#include <ida.hpp>
#include "winbase_debmod.h"
#ifndef __X86__
#define IDA_ADDRESS_SIZE 8
#else
#define IDA_ADDRESS_SIZE 4
#endif
const TCHAR kernel32_dll[] = TEXT("kernel32.dll");
typedef BOOL WINAPI GetProcessDEPPolicy_t(HANDLE hProcess, LPDWORD lpFlags, PBOOL lpPermanent);
static GetProcessDEPPolicy_t *_GetProcessDEPPolicy = NULL;
typedef dep_policy_t WINAPI GetSystemDEPPolicy_t(void);
static GetSystemDEPPolicy_t *_GetSystemDEPPolicy = NULL;
//--------------------------------------------------------------------------
winbase_debmod_t::winbase_debmod_t(void)
{
HMODULE k32 = GetModuleHandle(kernel32_dll);
if ( _GetProcessDEPPolicy == NULL )
*(FARPROC*)&_GetProcessDEPPolicy = GetProcAddress(k32, TEXT("GetProcessDEPPolicy"));
if ( _GetSystemDEPPolicy == NULL )
*(FARPROC*)&_GetSystemDEPPolicy = GetProcAddress(k32, TEXT("GetSystemDEPPolicy"));
if ( _GetSystemDEPPolicy != NULL )
dep_policy = _GetSystemDEPPolicy();
win_tool_help = NULL;
set_platform("win32");
}
//--------------------------------------------------------------------------
// Prepare new page protections for a breakpoint of BPTTYPE.
// Use INPUT as starting page protections.
// Return false in the case of failure.
bool winbase_debmod_t::remove_page_protections(
DWORD *p_input,
bpttype_t bpttype,
dep_policy_t dpolicy,
HANDLE proc_handle)
{
// If PAGE_GUARD is already set, do not change anything, it is already ok
DWORD input = *p_input;
if ( (input & PAGE_GUARD) != 0 )
return false;
// Convert between Unix permissions and Win32 page protections using this array:
static const uchar win32_page_protections[] =
{
PAGE_NOACCESS, // 000
PAGE_READONLY, // 001
0xFF, // 010 WRITE_ONLY does not exist on win32
PAGE_READWRITE, // 011
PAGE_EXECUTE, // 100
PAGE_EXECUTE_READ, // 101
0xFF, // 110 EXECUTE_WRITE does not exist on win32
PAGE_EXECUTE_READWRITE, // 111
};
uchar unix;
// convert ..COPY page protections into their non-copy counterparts
// this is the best thing we can do with them because they are automatically
// converted by the system upon a write access
if ( (input & PAGE_WRITECOPY) != 0 )
{
unix = 3; // rw
}
else if ( (input & PAGE_EXECUTE_WRITECOPY) != 0 )
{
unix = 7; // rwx
}
else
{
for ( unix=0; unix < 8; unix++ )
{
uchar p = win32_page_protections[unix];
if ( p != 0xFF && (input & p) != 0 )
break;
}
}
QASSERT(622, unix < 8);
// convert bpttype into unix permissions
int del = 0;
if ( (bpttype & BPT_READ) != 0 )
del |= 1;
if ( (bpttype & BPT_WRITE) != 0 )
del |= 2;
if ( (bpttype & BPT_EXEC) != 0 )
{
del |= 4;
// if DEP is disabled for this process then a program can
// happily execute code in a read only area so we need to
// remove *all* privileges, unfortunately
if ( dpolicy != dp_always_on )
{
// on XP, GetProcessDEPPolicy returns DEP policy for current process (i.e. the debugger)
// so we can't use it
// assume that DEP is disabled by default
DWORD flags = 0;
BOOL permanent = 0;
if ( _GetProcessDEPPolicy == NULL
|| winver.is_strictly_xp()
|| winver.is_GetProcessDEPPolicy_broken()
|| _GetProcessDEPPolicy(proc_handle, &flags, &permanent) )
{
// flags == 0: DEP is disabled for the specified process.
//
// Remarks: if permanent == 0 and global DEP policy is OptIn
// flags may be equal to 1 *but* having DEP disabled because,
// in case the process called SetProcessDEPPolicy the
// permanent argument would be 1, it seems to be a bug in the
// documentation
if ( (dpolicy == dp_opt_in && permanent == 0) || flags == 0 )
del |= 1;
}
}
}
// Remove the access types to trigger on
unix &= ~del;
// Handle WRITE_ONLY and EXECUTE_WRITE cases because win32 does not have them.
// We use stricter page permissions for them. This means that there will
// be more useless exceptions but we cannot do much about it.
if ( unix == 2 || unix == 6 )
unix = 0; // use PAGE_NOACCESS instead of WRITE_ONLY or EXECUTE_WRITE
uchar perm = win32_page_protections[unix];
*p_input = (input & ~0xFF) | perm;
return true;
}
//--------------------------------------------------------------------------
bool idaapi winbase_debmod_t::dbg_enable_page_bpt(
page_bpts_t::iterator p,
bool enable)
{
pagebpt_data_t &bpt = p->second;
if ( (bpt.old_prot != 0) == enable )
return false; // already the desired state
debdeb("dbg_enable_page_bpt(%s): page_ea=%a, old_prot=0x%x, new_prot=0x%x\n", enable ? "true" : "false", bpt.page_ea, bpt.old_prot, bpt.new_prot);
DWORD old;
DWORD prot = enable ? bpt.new_prot : bpt.old_prot;
if ( !VirtualProtectEx(process_handle, (void*)(size_t)bpt.page_ea,
bpt.real_len, prot, &old) )
{
deberr("VirtualProtectEx");
// if the page disappeared while disabling a bpt, do not complain,
// silently return success
if ( enable )
return false;
old = 0;
}
debdeb(" success! old=0x%x\n", old);
bpt.old_prot = enable ? old : 0;
return true;
}
//--------------------------------------------------------------------------
// Should we generate a BREAKPOINT event because of page bpt?
//lint -e{1746} could be made const reference
bool should_fire_page_bpt(
page_bpts_t::iterator p,
ea_t ea,
DWORD failed_access_type,
ea_t pc,
dep_policy_t dep_policy)
{
const pagebpt_data_t &bpt = p->second;
if ( !interval::contains(bpt.ea, bpt.user_len, ea) )
return false; // not in the user-defined interval
int bit;
switch ( failed_access_type )
{
default:
INTERR(623); //-V796 no break
case EXCEPTION_READ_FAULT: // failed READ access
// depending on the DEP policy we mark this access also
// to be triggered in case of EXEC breakpoints
bit = BPT_READ;
if ( dep_policy != dp_always_on && bpt.type == BPT_EXEC && pc == ea )
bit |= BPT_EXEC;
break;
case EXCEPTION_WRITE_FAULT: // failed WRITE access
bit = BPT_WRITE;
break;
case EXCEPTION_EXECUTE_FAULT: // failed EXECUTE access
bit = BPT_EXEC;
break;
}
return (bpt.type & bit) != 0;
}
//--------------------------------------------------------------------------
// returns 0-failure, 2-success
int idaapi winbase_debmod_t::dbg_add_page_bpt(
bpttype_t type,
ea_t ea,
int size)
{
// only one page breakpoint per page is permitted
page_bpts_t::iterator p = find_page_bpt(ea, size);
if ( p != page_bpts.end() )
return 0; // another page bpt exists
// Find out the current page protections
MEMORY_BASIC_INFORMATION meminfo;
ea_t page_ea = calc_page_base(ea);
if ( !VirtualQueryEx(process_handle, (void *)(size_t)page_ea,
&meminfo, sizeof(meminfo)) )
{
deberr("VirtualQueryEx");
return 0;
}
// Make sure the page is loaded
if ( (meminfo.State & MEM_FREE) != 0 )
{
deberr("%a: the page has not been allocated", page_ea);
return 0;
}
// According to MSDN documentation for VirtualQueryEx
// (...)
// AllocationProtect
// The memory protection option when the region was initially allocated. This member can be
// one of the memory protection constants or 0 if the caller does not have access.
//
// Unfortunately, there is no more information about why it my happen so, for now, I'm just
// returning an error.
if ( meminfo.Protect == 0 )
{
deberr("%a: the page cannot be accessed", page_ea);
return 0;
}
// Calculate new page protections
int aligned_len = align_up((ea-page_ea)+size, MEMORY_PAGE_SIZE);
int real_len = 0;
DWORD prot = meminfo.Protect;
if ( remove_page_protections(&prot, type, dep_policy, process_handle) )
{ // We have to set new protections
real_len = aligned_len;
}
// Remember the new breakpoint
p = page_bpts.insert(std::make_pair(page_ea, pagebpt_data_t())).first;
pagebpt_data_t &bpt = p->second;
bpt.ea = ea;
bpt.user_len = size;
bpt.page_ea = page_ea;
bpt.aligned_len = aligned_len;
bpt.real_len = real_len;
bpt.old_prot = 0;
bpt.new_prot = prot;
bpt.type = type;
// for PAGE_GUARD pages, no need to change the permissions, everything is fine already
if ( real_len == 0 )
{
bpt.old_prot = meminfo.Protect;
return 2;
}
return dbg_enable_page_bpt(p, true) ? 2 : 0;
}
//--------------------------------------------------------------------------
// returns true if changed *protect (in other words, if we have to mask
// the real page protections and return the original one)
bool winbase_debmod_t::mask_page_bpts(
ea_t startea,
ea_t endea,
uint32 *protect)
{
// if we have page breakpoints, what we return must be changed to show the
// real segment privileges, instead of the new ones we applied for the bpt
int newprot = 0;
page_bpts_t::iterator p = page_bpts.begin();
while ( p != page_bpts.end() )
{
pagebpt_data_t &pbd = p->second;
if ( pbd.page_ea + pbd.real_len > startea )
{
if ( pbd.page_ea >= endea )
break;
if ( pbd.old_prot != 0 )
{ // bpt has been written to the process memory
if ( *protect == pbd.new_prot )
{ // return the old protection, before setting the page bpt
newprot = pbd.old_prot;
}
else
{
debdeb("mask_page_bpts: app changed our page protection for %a (expected: 0x%x, actual: 0x%x)\n", pbd.page_ea, pbd.new_prot, *protect);
// page protection has been changed by the application
DWORD prot = *protect;
if ( prot == PAGE_WRITECOPY && pbd.new_prot == PAGE_READWRITE
|| prot == PAGE_EXECUTE_WRITECOPY && pbd.new_prot == PAGE_EXECUTE_READWRITE )
{
// in some cases OS may restore WRITECOPY protection; do nothing in such cases since it works the same way for breakpoint purposes
debdeb(" ignoring changes to WRITECOPY protection\n");
}
else if ( remove_page_protections(&prot, pbd.type, dep_policy, process_handle) )
{
pbd.new_prot = prot;
pbd.old_prot = 0; // mark our bpt as non-written
debdeb(" will re-set protection to 0x%x\n", pbd.new_prot);
}
}
}
}
++p;
}
if ( newprot != 0 )
{
*protect = newprot;
return true;
}
return false;
}
//--------------------------------------------------------------------------
// Page breakpoints modify the page protections to induce access violations.
// We must hide the modified page protections from IDA and report the original
// page protections.
// Second, the application may render a page bpt inactive by changing its page protections.
// In this case we must report to IDA the new page protections and also reactivate
// the page breakpoint.
void winbase_debmod_t::verify_page_protections(
meminfo_vec_t *areas,
const win32_prots_t &prots)
{
QASSERT(624, areas->size() == prots.size());
if ( page_bpts.empty() )
return;
for ( int i = 0; i < areas->size(); i++ )
{
uint32 prot = prots[i];
memory_info_t &a = areas->at(i);
if ( mask_page_bpts(a.start_ea, a.end_ea, &prot) )
a.perm = win_prot_to_ida_perm(prot);
}
// reactivate all disabled page bpts, if any
enable_page_bpts(true);
}
//--------------------------------------------------------------------------
#ifndef __X86__
wow64_state_t winbase_debmod_t::check_wow64_process()
{
if ( is_wow64 == WOW64_NONE )
{
is_wow64 = check_wow64_handle(process_handle);
if ( is_wow64 > 0 )
dmsg("WOW64 process has been detected (pid=%d)\n", pid);
}
return is_wow64;
}
#endif
//--------------------------------------------------------------------------
bool highdll_vec_t::has(eanat_t addr) const
{
for ( int i = 0; i < size(); ++i )
if ( (*this)[i].has(addr) )
return true;
return false;
}
//--------------------------------------------------------------------------
bool highdll_vec_t::add(eanat_t addr, size_t sz, HANDLE h)
{
if ( has(addr) )
return false;
// check removed: on new win10 we can have above 4GB:
// ntdll.dll, wow64.dll, wow64win.dll
// QASSERT(1491, size() < 2);
highdll_range_t &r = push_back();
r.start = addr;
r.end = addr + sz;
r.handle = h;
return true;
}
//--------------------------------------------------------------------------
bool highdll_vec_t::add_high_module(
eanat_t addr,
size_t sz,
HANDLE h)
{
if ( ea_t(addr) == addr )
return false;
add(addr, sz, h); //-V779 unreachable code
return true;
}
//--------------------------------------------------------------------------
bool highdll_vec_t::del_high_module(HANDLE *h, eanat_t addr)
{
for ( int i = 0; i < size(); ++i )
{
const highdll_range_t &r = (*this)[i];
if ( r.start == addr )
{
if ( h != NULL )
*h = r.handle;
erase(begin() + i);
return ea_t(addr) != addr;
}
}
return false;
}
//--------------------------------------------------------------------------
void idaapi winbase_debmod_t::dbg_term(void)
{
is_wow64 = WOW64_NONE;
delete win_tool_help;
win_tool_help = NULL;
}
//-------------------------------------------------------------------------
bool winbase_debmod_t::handle_process_start(pid_t _pid)
{
debapp_attrs.addrsize = get_process_addrsize(_pid);
is64 = debapp_attrs.addrsize == 8;
term_reg_ctx();
init_reg_ctx();
return true;
}
//-------------------------------------------------------------------------
void winbase_debmod_t::cleanup(void)
{
inherited::cleanup();
is64 = false;
}
//--------------------------------------------------------------------------
// Check if we need to install a temporary breakpoint to workaround the
// 'freely running after syscall' problem. Exactly, the problem is the
// following: after single stepping over a "jmp far ptr" instruction in
// wow64cpu.dll for a 32bits process under a 64bits OS (Win7), the trap flag
// is lost. Probably, it's a bug in wow64cpu!CpuReturnFromSimulatedCode.
//
// So, if we find an instruction like "call large dword fs:XX" we add a
// temporary breakpoint at the next instruction and re-enable tracing
// when the breakpoint is reached.
bool winbase_debmod_t::check_for_call_large(
const debug_event_t *event,
HANDLE handle)
{
if ( check_wow64_handle(handle) <= 0 )
return false;
uchar buf[3];
if ( dbg_read_memory(event->ea, buf, 3, NULL) == 3 )
{
// is it the call large instruction?
if ( memcmp(buf, "\x64\xFF\x15", 3) == 0 )
return true;
}
return false;
}
//--------------------------------------------------------------------------
// Get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
int idaapi winbase_debmod_t::get_process_bitness(int _pid)
{
if ( _pid != -1 && _pid != GetCurrentProcessId() )
{
if ( !winver.is_64bitOS() )
return 4;
switch ( check_wow64_pid(_pid) )
{
case WOW64_BAD: return 0; // bad process id
case WOW64_YES: return 4; // wow64 process, 32bit
case WOW64_NO: return 8; // regular 64bit process
default: break;
}
}
return IDA_ADDRESS_SIZE;
}
//--------------------------------------------------------------------------
static const char *str_bitness(int addrsize)
{
switch ( addrsize )
{
case 8:
return "[64]";
case 4:
return "[32]";
default:
return "[x]";
}
}
//--------------------------------------------------------------------------
// this function may correct pinfo->addrsize
bool winbase_debmod_t::get_process_path(
ext_process_info_t *pinfo,
char *buf,
size_t bufsize)
{
module_snapshot_t msnap(get_tool_help());
MODULEENTRY32 me;
if ( !msnap.first(TH32CS_SNAPMODULE, pinfo->pid, &me) )
{
if ( msnap.last_err() == ERROR_PARTIAL_COPY && pinfo->addrsize == 0 )
{
// MSDN: If the specified process is a 64-bit process and the caller is a
// 32-bit process, error code is ERROR_PARTIAL_COPY
pinfo->addrsize = 8;
}
qstrncpy(buf, pinfo->name.c_str(), bufsize);
return false;
}
else
{
tchar_utf8(buf, me.szExePath, bufsize);
return true;
}
}
//--------------------------------------------------------------------------
win_tool_help_t *winbase_debmod_t::get_tool_help()
{
if ( win_tool_help == NULL )
win_tool_help = new win_tool_help_t;
return win_tool_help;
}
//-------------------------------------------------------------------------
int winbase_debmod_t::get_process_addrsize(pid_t _pid)
{
int addrsize = get_process_bitness(_pid);
return addrsize != 0 ? addrsize : IDA_ADDRESS_SIZE;
}
//--------------------------------------------------------------------------
//lint -e{1762} could be made const [in fact it cannot be made const in x64 mode]
bool winbase_debmod_t::is_ntdll_name(const char *path)
{
const char *base_name = qbasename(path);
const char *ntdll_name = winver.is_NT()
? "ntdll.dll" // NT
: "kernel32.dll"; // 9X/Me and KERNEL32.DLL
if ( strieq(base_name, ntdll_name) )
return true;
#ifndef __X86__
if ( winver.is_NT()
&& check_wow64_process() == WOW64_YES
&& strieq(base_name, "ntdll32.dll") )
{
return true;
}
#endif
return false;
}
//--------------------------------------------------------------------------
//lint -esym(1762,winbase_debmod_t::build_process_ext_name) could be made const
void winbase_debmod_t::build_process_ext_name(ext_process_info_t *pinfo)
{
char fullname[MAXSTR];
if ( get_process_path(pinfo, fullname, sizeof(fullname))
&& pinfo->addrsize == 0 )
{
// the WOW64 is optional on R2 x64 server
pinfo->addrsize = IDA_ADDRESS_SIZE;
}
pinfo->ext_name = str_bitness(pinfo->addrsize);
if ( !pinfo->ext_name.empty() )
pinfo->ext_name += ' ';
pinfo->ext_name += fullname;
}
//--------------------------------------------------------------------------
int idaapi winbase_debmod_t::get_process_list(procvec_t *list, qstring *)
{
int mypid = GetCurrentProcessId();
list->clear();
process_snapshot_t psnap(get_tool_help());
PROCESSENTRY32 pe32;
for ( bool ok = psnap.first(TH32CS_SNAPNOHEAPS, &pe32); ok; ok = psnap.next(&pe32) )
{
if ( pe32.th32ProcessID != mypid )
{
int addrsize = get_process_bitness(pe32.th32ProcessID);
#ifndef __EA64__
if ( addrsize > 4 )
continue; // skip 64bit processes, we cannot debug them because ea_t is 32bit
#endif
ext_process_info_t pinfo;
pinfo.pid = pe32.th32ProcessID;
pinfo.addrsize = addrsize;
tchar_utf8(&pinfo.name, pe32.szExeFile);
build_process_ext_name(&pinfo);
list->push_back(pinfo);
}
}
return list->size();
}
//--------------------------------------------------------------------------
// Returns the file name assciated with pid
bool idaapi winbase_debmod_t::get_exec_fname(int _pid, char *buf, size_t bufsize)
{
ext_process_info_t pinfo;
pinfo.pid = _pid;
pinfo.name.qclear();
return get_process_path(&pinfo, buf, bufsize);
}
//--------------------------------------------------------------------------
win_tool_help_t *winbase_debmod_t::win_tool_help = NULL;
win_version_t winbase_debmod_t::winver;

View File

@@ -0,0 +1,185 @@
#ifndef __WINBASE_HPP__
#define __WINBASE_HPP__
// Base class for win32 and windbg modules
using std::for_each;
using std::pair;
using std::make_pair;
//--------------------------------------------------------------------------
#define BASE_DEBUGGER_MODULE pc_debmod_t
#include "deb_pc.hpp"
#include "pc_debmod.h"
#define BPT_CODE_SIZE X86_BPT_SIZE
#include "win32_util.hpp"
extern const TCHAR kernel32_dll[];
//--------------------------------------------------------------------------
// DEP policies
enum dep_policy_t
{
dp_always_off,
dp_always_on,
dp_opt_in,
dp_opt_out
};
//--------------------------------------------------------------------------
enum attach_status_t
{
as_none, // no attach to process requested
as_attaching, // waiting for CREATE_PROCESS_DEBUG_EVENT, indicating the process is attached
as_breakpoint, // waiting for first breakpoint, indicating the process was properly initialized and suspended
as_attached, // process was successfully attached
as_detaching, // waiting for next get_debug_event() request, to return the process as detached
as_attach_kernel, // attaching to kernel
};
// vector of win32 page protections
// we need this type because meminfo_t does not contain the original win32 protections
// but we need them to verify page bpts
typedef qvector<uint32> win32_prots_t;
//--------------------------------------------------------------------------
// When debugging WOW64 processes with ida32 we have to take into account
// ntdll.dll (and wow64*.dll), which are x64 files
// that can be loaded into high addresses (above 4GB)
// Since ea_t cannot represent such addresses,
// we use our own type to remember the DLL boundaries
typedef size_t eanat_t;
struct highdll_range_t
{
eanat_t start;
eanat_t end;
HANDLE handle;
highdll_range_t() : start(0), end(0), handle(INVALID_HANDLE_VALUE) {}
bool has(eanat_t addr) const { return addr >= start && addr < end; }
};
DECLARE_TYPE_AS_MOVABLE(highdll_range_t);
struct highdll_vec_t : protected qvector<highdll_range_t>
{
private:
size_t num_ntdlls; // count of actual ntdll*.dll modules in the list
public:
typedef qvector<highdll_range_t> inherited;
highdll_vec_t() : num_ntdlls(0) {}
void clear() { inherited::clear(); num_ntdlls = 0; }
size_t size() const { return inherited::size(); }
size_t count_ntdlls() const { return num_ntdlls; }
bool empty() const { return inherited::empty(); }
// return false if there is already a dll with such an address
bool add(eanat_t addr, size_t size, HANDLE h = INVALID_HANDLE_VALUE);
bool add_ntdll(eanat_t addr, size_t size, HANDLE h = INVALID_HANDLE_VALUE)
{
bool ok = add(addr, size, h);
if ( ok )
num_ntdlls++;
return ok;
};
// it returns true if the dll address doesn't fit in `ea_t`
bool add_high_module(
eanat_t addr,
size_t size,
HANDLE h = INVALID_HANDLE_VALUE);
// it returns true if the dll address doesn't fit to `ea_t`
bool del_high_module(HANDLE *h, eanat_t addr);
bool has(eanat_t addr) const;
};
//--------------------------------------------------------------------------
class winbase_debmod_t: public BASE_DEBUGGER_MODULE
{
typedef BASE_DEBUGGER_MODULE inherited;
wow64_state_t is_wow64 = WOW64_NONE; // use check_wow64_process()
protected:
HANDLE process_handle = INVALID_HANDLE_VALUE;
dep_policy_t dep_policy = dp_always_off;
highdll_vec_t highdlls;
bool is64 = false;
// local functions
bool mask_page_bpts(ea_t startea, ea_t endea, uint32 *protect);
void verify_page_protections(meminfo_vec_t *areas, const win32_prots_t &prots);
winbase_debmod_t(void);
// overridden virtual functions
bool idaapi dbg_enable_page_bpt(page_bpts_t::iterator p, bool enable);
int idaapi dbg_add_page_bpt(bpttype_t type, ea_t ea, int size);
bool check_for_call_large(const debug_event_t *event, HANDLE process_handle);
#ifndef __X86__
wow64_state_t check_wow64_process();
#else
wow64_state_t check_wow64_process() { return WOW64_NO; }
#endif
int get_process_addrsize(pid_t pid);
bool is_ntdll_name(const char *path);
// return number of processes, -1 - not implemented
virtual int idaapi get_process_list(procvec_t *proclist, qstring *errbuf) override;
// return the file name assciated with pid
virtual bool idaapi get_exec_fname(int pid, char *buf, size_t bufsize) newapi;
// get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
virtual int idaapi get_process_bitness(int pid) newapi;
virtual void init_reg_ctx(void) newapi {}
virtual void term_reg_ctx(void) newapi {}
public:
virtual void idaapi dbg_term(void) override;
static win_tool_help_t *get_tool_help();
static win_version_t winver;
protected:
bool handle_process_start(pid_t _pid);
void cleanup(void);
private:
void build_process_ext_name(ext_process_info_t *pinfo);
static bool get_process_path(
ext_process_info_t *pinfo,
char *buf,
size_t bufsize);
static bool remove_page_protections(
DWORD *p_input,
bpttype_t bpttype,
dep_policy_t dpolicy,
HANDLE proc_handle);
static win_tool_help_t *win_tool_help;
};
bool should_fire_page_bpt(page_bpts_t::iterator p, ea_t ea, DWORD failed_access_type, ea_t pc, dep_policy_t dep_policy);
#ifdef _PE_H_
bool read_pe_header(peheader_t *pe);
#endif
//-------------------------------------------------------------------------
inline void tchar_utf8(qstring *buf, TCHAR *tchar)
{
#ifdef UNICODE
utf16_utf8(buf, tchar);
#else
acp_utf8(buf, tchar);
#endif
}
//-------------------------------------------------------------------------
inline void tchar_utf8(char *buf, TCHAR *tchar, size_t bufsize)
{
qstring utf8;
tchar_utf8(&utf8, tchar);
qstrncpy(buf, utf8.c_str(), bufsize);
}
#endif

131
idasdk76/defaults.mk Normal file
View File

@@ -0,0 +1,131 @@
#############################################################################
# versions and paths for various external libraries and utils
ifdef __NT__
# The following variables may have been set by vcvars.bat. You may
# also set them manually. The default installation directories are
# defined below in case these variables are not set.
# Note: the following paths use backslashes (and may also contain a
# trailing backslash) in order to conform to the variables
# exported by vcvars.bat.
# Visual C++ 2017 Install Directory
VCINSTALLDIR ?= '$(PROGRAMFILES)\Microsoft Visual Studio\2017\Professional\VC\'
# Visual C++ 2017 Tools Version
# Note: if this variable is not set, the default version is obtained
# in allmake.mak under "Visual C++ 2017 Tools Version".
# VCToolsVersion ?= '14.11.25503'
# Windows SDK Install Directory
WindowsSdkDir ?= '$(PROGRAMFILES)\Windows Kits\10\'
# Windows SDK version
# Note: if this variable is not set, the latest version is detected
# in allmake.mak under "Windows SDK Version".
# WindowsSDKVersion ?= '10.0.17134.0\'
# Microsoft SDK v7.1A is only used for the win32 debugger server for
# Windows XP compatibility.
MSSDK71_PATH = '$(PROGRAMFILES)/Microsoft SDKs/Windows/v7.1A'
else ifdef __MAC__
# oldest supported version of MacOSX
ifdef __ARM__
MACOSX_DEPLOYMENT_TARGET = 11.0
else
MACOSX_DEPLOYMENT_TARGET = 10.9
endif
endif
# Python
PYTHON_VERSION_MAJOR?=3
PYTHON_VERSION_MINOR?=4
PYTHON_VERNAME=python$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)
# TODO clean this up
ifdef __NT__
ifneq (,$(wildcard /cygdrive/c/Program\ Files/Python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR)/python.exe))
PYTHON_ROOT ?= C:/Program Files/Python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR)
else
ifeq ($(PYTHON_VERSION_MAJOR),2)
PYTHON_VERSUF=-x64
endif
PYTHON_ROOT ?= $(SYSTEMDRIVE)/Python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR)$(PYTHON_VERSUF)
endif
PYTHON ?= "$(PYTHON_ROOT)/python.exe"
else
PYTHON ?= $(PYTHON_VERNAME)
endif
# Qt
QTPROC-1=x64
QTPROC-$(__ARM__)=arm64
QTVER?=5.6.3-$(QTPROC-1)
QTDIR-$(__LINUX__) = /usr/local/Qt/$(QTVER)/
QTDIR-$(__MAC__) = /Users/Shared/Qt/$(QTVER)/
QTDIR-$(__NT__) = $(SYSTEMDRIVE)/Qt/$(QTVER)/
QTDIR ?= $(QTDIR-1)
ifdef __NT__
ifdef NDEBUG
QTSUFF=.dll
else
QTSUFF=d.dll
endif
QTLIBDIR=bin
else ifdef __LINUX__
QTPREF=lib
QTSUFF=.so.5
QTLIBDIR=lib
endif
# SWiG
ifeq ($(PYTHON_VERSION_MAJOR),3)
SWIG_VERSION?=4.0.1
ifdef __NT__
SWIG_DIR_SUFFIX?=-py3-stable-abi-cygwin
else
SWIG_DIR_SUFFIX?=-py3-stable-abi
endif
else
SWIG_VERSION?=4.0.0
endif
ifdef __NT__
ifeq ($(PYTHON_VERSION_MAJOR),3)
SWIG_DISTRIBUTION_HAS_UNIX_LAYOUT:=1
endif
else
SWIG_DISTRIBUTION_HAS_UNIX_LAYOUT:=1
endif
ifeq ($(SWIG_DISTRIBUTION_HAS_UNIX_LAYOUT),1)
ifdef USE_CCACHE
# we set CCACHE_DIR so as to not interfere with the system's ccache
# and we set CCACHE_CPP2 to prevent SWiG from printing a bunch of
# warnings due to re-using of the preprocessed source.
SWIG?=CCACHE_DIR='$${HOME}/.ccache-swig' CCACHE_CPP2=1 $(SWIG_HOME)/bin/ccache-swig $(SWIG_HOME)/bin/swig
else
SWIG?=$(SWIG_HOME)/bin/swig
endif
SWIG_INCLUDES?=-I$(SWIG_HOME)/share/swig/$(SWIG_VERSION)/python -I$(SWIG_HOME)/share/swig/$(SWIG_VERSION)
else
SWIG?=$(SWIG_HOME)/swig.exe
SWIG_INCLUDES?=-I$(SWIG_HOME)/Lib/python -I$(SWIG_HOME)/Lib
endif
#############################################################################
# keep all paths in unix format, with forward slashes
ifeq ($(OS),Windows_NT)
# define: convert dos path to unix path by replacing backslashes by slashes
unixpath=$(subst \,/,$(1))
PYTHON_ROOT :=$(call unixpath,$(PYTHON_ROOT))
PYTHON :=$(call unixpath,$(PYTHON))
SWIG :=$(call unixpath,$(SWIG))
QTDIR :=$(call unixpath,$(QTDIR))
endif
#############################################################################
# http://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile
.print-% : ; @echo $($*)

21420
idasdk76/include/allins.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
// this file should be included before calling deprecated functions
// it should be included at the point where the definitions of deprecated
// functions begin in the source file. this way a deprecated function may call
// another deprecated function without raising a warning.
// deprecated functions may call each other
#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

289
idasdk76/include/auto.hpp Normal file
View File

@@ -0,0 +1,289 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef _AUTO_HPP
#define _AUTO_HPP
#include <ida.hpp>
/*! \file auto.hpp
\brief Functions that work with the autoanalyzer queue.
The autoanalyzer works when IDA is not busy processing
the user keystrokes. It has several queues, each queue having
its own priority. The analyzer stops when all queues are empty.
A queue contains addresses or address ranges.
The addresses are kept sorted by their values.
The analyzer will process all addresses from the first queue, then
switch to the second queue and so on.
There are no limitations on the size of the queues.
This file also contains functions that deal with the IDA status
indicator and the autoanalysis indicator.
You may use these functions to change the indicator value.
*/
typedef int atype_t; ///< identifies an autoanalysis queue - see \ref AU_
/// \defgroup AU_ Autoanalysis queues
/// Names and priorities of the analyzer queues
//@{
const atype_t
AU_NONE = 00, ///< placeholder, not used
AU_UNK = 10, ///< 0: convert to unexplored
AU_CODE = 20, ///< 1: convert to instruction
AU_WEAK = 25, ///< 2: convert to instruction (ida decision)
AU_PROC = 30, ///< 3: convert to procedure start
AU_TAIL = 35, ///< 4: add a procedure tail
AU_FCHUNK=38, ///< 5: find func chunks
AU_USED = 40, ///< 6: reanalyze
AU_TYPE = 50, ///< 7: apply type information
AU_LIBF = 60, ///< 8: apply signature to address
AU_LBF2 = 70, ///< 9: the same, second pass
AU_LBF3 = 80, ///< 10: the same, third pass
AU_CHLB = 90, ///< 11: load signature file (file name is kept separately)
AU_FINAL=200; ///< 12: final pass
//@}
typedef int idastate_t; ///< IDA status indicator - see \ref st_
/// \defgroup st_ Status indicator states
//@{
const idastate_t
// meaning
st_Ready = 0, ///< READY: IDA is doing nothing
st_Think = 1, ///< THINKING: Autoanalysis on, the user may press keys
st_Waiting = 2, ///< WAITING: Waiting for the user input
st_Work = 3; ///< BUSY: IDA is busy
//@}
/// Get current state of autoanalyzer.
/// If auto_state == ::AU_NONE, IDA is currently not running the analysis
/// (it could be temporarily interrupted to perform the user's requests, for example).
idaman atype_t ida_export get_auto_state(void);
/// Set current state of autoanalyzer.
/// \param new_state new state of autoanalyzer
/// \return previous state
idaman atype_t ida_export set_auto_state(atype_t new_state);
/// See ::get_auto_display
struct auto_display_t
{
atype_t type = AU_NONE;
ea_t ea = BADADDR;
idastate_t state = st_Ready;
};
/// Get structure which holds the autoanalysis indicator contents
idaman bool ida_export get_auto_display(auto_display_t *auto_display);
/// Change autoanalysis indicator value.
/// \param ea linear address being analyzed
/// \param type autoanalysis type (see \ref AU_)
idaman void ida_export show_auto(ea_t ea, atype_t type=AU_NONE);
/// Show an address on the autoanalysis indicator.
/// The address is displayed in the form " @:12345678".
/// \param ea - linear address to display
inline void show_addr(ea_t ea) { show_auto(ea); }
/// Change IDA status indicator value
/// \param st - new indicator status
/// \return old indicator status
idaman idastate_t ida_export set_ida_state(idastate_t st);
/// Is it allowed to create stack variables automatically?.
/// This function should be used by IDP modules before creating stack vars.
inline bool may_create_stkvars(void)
{
return inf_should_create_stkvars() && get_auto_state() == AU_USED;
}
/// Is it allowed to trace stack pointer automatically?.
/// This function should be used by IDP modules before tracing sp.
inline bool may_trace_sp(void)
{
if ( inf_should_trace_sp() )
{
atype_t auto_state = get_auto_state();
return auto_state == AU_USED;
}
return false;
}
/// Put range of addresses into a queue.
/// 'start' may be higher than 'end', the kernel will swap them in this case.
/// 'end' doesn't belong to the range.
idaman void ida_export auto_mark_range(ea_t start,ea_t end,atype_t type);
/// Put single address into a queue. Queues keep addresses sorted.
inline void auto_mark(ea_t ea, atype_t type)
{
auto_mark_range(ea, ea+1, type);
}
/// Remove range of addresses from a queue.
/// 'start' may be higher than 'end', the kernel will swap them in this case.
/// 'end' doesn't belong to the range.
idaman void ida_export auto_unmark(ea_t start, ea_t end, atype_t type);
// Convenience functions
/// Plan to perform reanalysis
inline void plan_ea(ea_t ea)
{
auto_mark(ea, AU_USED);
}
/// Plan to perform reanalysis
inline void plan_range(ea_t sEA, ea_t eEA)
{
auto_mark_range(sEA, eEA, AU_USED);
}
/// Plan to make code
inline void auto_make_code(ea_t ea)
{
auto_mark(ea, AU_CODE);
}
/// Plan to make code&function
inline void auto_make_proc(ea_t ea)
{
auto_make_code(ea);
auto_mark(ea, AU_PROC);
}
/// Plan to reanalyze callers of the specified address.
/// This function will add to ::AU_USED queue all instructions that
/// call (not jump to) the specified address.
/// \param ea linear address of callee
/// \param noret !=0: the callee doesn't return, mark to undefine subsequent
/// instructions in the caller. 0: do nothing.
idaman void ida_export reanalyze_callers(ea_t ea, bool noret);
/// Delete all analysis info that IDA generated for for the given range
idaman void ida_export revert_ida_decisions(ea_t ea1, ea_t ea2);
/// Plan to apply the callee's type to the calling point
idaman void ida_export auto_apply_type(ea_t caller, ea_t callee);
/// Plan to apply the tail_ea chunk to the parent
/// \param tail_ea linear address of start of tail
/// \param parent_ea linear address within parent. If BADADDR, automatically
/// try to find parent via xrefs.
idaman void ida_export auto_apply_tail(ea_t tail_ea, ea_t parent_ea);
/// Analyze the specified range.
/// Try to create instructions where possible.
/// Make the final pass over the specified range if specified.
/// This function doesn't return until the range is analyzed.
/// \retval 1 ok
/// \retval 0 Ctrl-Break was pressed
idaman int ida_export plan_and_wait(ea_t ea1, ea_t ea2, bool final_pass=true);
/// Process everything in the queues and return true.
/// \return false if the user clicked cancel.
/// (the wait box must be displayed by the caller if desired)
idaman bool ida_export auto_wait(void);
/// Process everything in the specified range and return true.
/// \return number of autoanalysis steps made. -1 if the user clicked cancel.
/// (the wait box must be displayed by the caller if desired)
idaman ssize_t ida_export auto_wait_range(ea_t ea1, ea_t ea2);
/// Analyze one address in the specified range and return true.
/// \return if processed anything. false means that there is nothing to
/// process in the specified range.
idaman bool ida_export auto_make_step(ea_t ea1, ea_t ea2);
/// Remove an address range (ea1..ea2) from queues ::AU_CODE, ::AU_PROC, ::AU_USED.
/// To remove an address range from other queues use auto_unmark() function.
/// 'ea1' may be higher than 'ea2', the kernel will swap them in this case.
/// 'ea2' doesn't belong to the range.
idaman void ida_export auto_cancel(ea_t ea1, ea_t ea2);
/// Are all queues empty?
/// (i.e. has autoanalysis finished?).
idaman bool ida_export auto_is_ok(void);
/// Peek into a queue 'type' for an address not lower than 'low_ea'.
/// Do not remove address from the queue.
/// \return the address or #BADADDR
idaman ea_t ida_export peek_auto_queue(ea_t low_ea, atype_t type);
/// Retrieve an address from queues regarding their priority.
/// Returns #BADADDR if no addresses not lower than 'lowEA' and less than
/// 'highEA' are found in the queues.
/// Otherwise *type will have queue type.
idaman ea_t ida_export auto_get(atype_t *type, ea_t lowEA, ea_t highEA);
/// Try to create instruction
/// \param ea linear address of callee
/// \return the length of the instruction or 0
idaman int ida_export auto_recreate_insn(ea_t ea);
/// Get autoanalyzer state
idaman bool ida_export is_auto_enabled(void);
/// Temporarily enable/disable autoanalyzer. Not user-facing, but rather because
/// IDA sometimes need to turn AA on/off regardless of inf.s_genflags:INFFL_AUTO
/// \return old state
idaman bool ida_export enable_auto(bool enable);
#endif // _AUTO_HPP

View File

@@ -0,0 +1,350 @@
#ifndef _BITMASK_HPP
#define _BITMASK_HPP
/*! \file bitrange.hpp
\brief Definition of the ::bitrange_t class
*/
//---------------------------------------------------------------------------
/// This class manages the offset and size of a value that occupies
/// a number of contiguous bits within some container - generally a byte array.
/// A special state - empty range (both offset and size are zeroes) - determines
/// the value as all bits of the container.
class bitrange_t
{
public:
/// Constructor
explicit bitrange_t(uint16 bit_ofs = 0, uint16 size_in_bits = 0);
/// Initialize offset and size to given values
inline void init(uint16 bit_ofs, uint16 size_in_bits);
/// Make the bitrange empty
inline void reset();
/// Is the bitrange empty?
inline bool empty() const;
/// Get offset of 1st bit
inline uint bitoff() const;
/// Get size of the value in bits
inline uint bitsize() const;
/// Size of the value in bytes
inline uint bytesize() const;
/// Convert to mask of 64 bits
inline uint64 mask64() const;
/// Does have common bits with another bitrange?
inline bool has_common(const bitrange_t &r) const;
/// Apply mask to a bitrange
/// \param subrange range *inside* the main bitrange to keep
/// After this operation the main bitrange will be truncated
/// to have only the bits that are specified by subrange.
/// Example: [off=8,nbits=4], subrange[off=1,nbits=2] => [off=9,nbits=2]
/// \return success
inline bool apply_mask(const bitrange_t &subrange);
/// Intersect two ranges
inline void intersect(const bitrange_t &r);
/// Create union of 2 ranges including the hole between them
inline void create_union(const bitrange_t &r);
/// Subtract a bitrange
inline bool sub(const bitrange_t &r);
/// Shift range down (left)
inline void shift_down(uint cnt);
/// Shift range up (right)
inline void shift_up(uint cnt);
/// Initialize bitrange_t with offset/size defined by given mask
template <class T> inline void assign_max_nonzero(T mask);
/// \name Extract
/// Extract a value from 'src' according to the bitrange
/// \param dst vector the extracted value will be stored to
/// \param src source buffer
/// \param src_size size of source buffer
/// \param is_mf is Msb First? (TRUE-big endian, FALSE-little endian)
//@{
inline bool extract(
bytevec_t *dst,
const void *src,
size_t src_size,
bool is_mf) const;
inline bool extract(
void *dst,
size_t dst_size,
const void *src,
size_t src_size,
bool is_mf) const;
//@}
/// \name Inject
/// Inject a value into 'dst' according to the bitrange
/// \param dst a buffer the value will be injected into
/// \param dst_size size of buffer
/// \param src source value
/// \param is_mf is Msb First? (TRUE-big endian, FALSE-little endian)
//@{
inline bool inject(
void *dst,
size_t dst_size,
const void *src,
size_t src_size,
bool is_mf) const;
inline bool inject(
void *dst,
size_t dst_size,
const bytevec_t &src,
bool is_mf) const;
//@}
DECLARE_COMPARISONS(bitrange_t);
private:
uint16 offset; ///< offset of 1st bit starting with the lowest bit
uint16 nbits; ///< size of the value in bits
};
//---------------------------------------------------------------------------
inline bitrange_t::bitrange_t(uint16 bit_ofs, uint16 size_in_bits)
: offset(bit_ofs), nbits(size_in_bits)
{
}
//---------------------------------------------------------------------------
inline void bitrange_t::init(uint16 bit_ofs, uint16 size_in_bits)
{
offset = bit_ofs;
nbits = size_in_bits;
}
//---------------------------------------------------------------------------
inline void bitrange_t::reset()
{
init(0, 0);
}
//---------------------------------------------------------------------------
inline bool bitrange_t::empty() const
{
return nbits == 0;
}
//---------------------------------------------------------------------------
inline uint bitrange_t::bitoff() const
{
return offset;
}
//---------------------------------------------------------------------------
inline uint bitrange_t::bitsize() const
{
return nbits;
}
//---------------------------------------------------------------------------
inline uint bitrange_t::bytesize() const
{
return (nbits + 8-1) / 8;
}
//--------------------------------------------------------------------------
inline uint64 bitrange_t::mask64() const
{
return empty() ? 0 : (left_shift(uint64(1), nbits)-1) << offset;
}
//--------------------------------------------------------------------------
inline bool bitrange_t::apply_mask(const bitrange_t &submask)
{
if ( submask.bitoff() + submask.bitsize() > bitsize() )
return false;
init(bitoff() + submask.bitoff(), submask.bitsize());
return true;
}
//--------------------------------------------------------------------------
inline void bitrange_t::intersect(const bitrange_t &r)
{
uint16 e1 = offset + nbits;
uint16 e2 = r.offset + r.nbits;
uint16 e = qmin(e1, e2);
uint16 s = qmax(offset, r.offset);
if ( s > e )
{
s = 0;
e = 0;
}
init(s, e-s);
}
//--------------------------------------------------------------------------
inline bool bitrange_t::has_common(const bitrange_t &r) const
{
return interval::overlap(offset, nbits, r.offset, r.nbits);
}
//--------------------------------------------------------------------------
inline void bitrange_t::create_union(const bitrange_t &r)
{
uint16 e1 = offset + nbits;
uint16 e2 = r.offset + r.nbits;
uint16 e = qmax(e1, e2);
uint16 s = qmin(offset, r.offset);
init(s, e-s);
}
//--------------------------------------------------------------------------
inline bool bitrange_t::sub(const bitrange_t &r)
{
// r is in the middle of our bitrange, cannot handle this case
// because it would require 2 bitranges :/
uint16 end = offset + nbits;
uint16 rend = r.offset + r.nbits;
if ( r.offset > offset && rend < end )
return false;
if ( r.offset <= offset )
{
if ( rend > end )
{
reset();
}
else if ( rend > offset )
{
offset = rend;
nbits = end - offset;
}
}
else if ( r.offset < end )
{
nbits = r.offset - offset;
}
return true;
}
//--------------------------------------------------------------------------
inline int bitrange_t::compare(const bitrange_t &r) const
{
if ( offset != r.offset )
return offset < r.offset ? -1 : 1;
if ( nbits != r.nbits )
return nbits < r.nbits ? -1 : 1;
return 0;
}
//--------------------------------------------------------------------------
inline void bitrange_t::shift_down(uint cnt)
{
if ( cnt > offset )
{
cnt -= offset;
offset = 0;
if ( cnt > nbits )
nbits = 0;
else
nbits -= cnt;
}
else
{
offset -= cnt;
}
}
//--------------------------------------------------------------------------
inline void bitrange_t::shift_up(uint cnt)
{
offset += cnt;
}
//--------------------------------------------------------------------------
template <class T> inline void bitrange_t::assign_max_nonzero(T mask)
{
if ( mask == T(0) )
{
reset();
return;
}
int i = 0;
T bit = T(1);
for ( i=0; i < sizeof(T)*8; ++i, bit <<= 1 )
if ( (mask & bit) != 0 )
break;
offset = i;
i = sizeof(T)*8 - 1;
bit = left_shift(T(1), i);
while ( i >= offset )
{
if ( (mask & bit) != 0 )
break;
--i;
bit >>= 1;
}
nbits = i - offset + 1;
}
//--------------------------------------------------------------------------
#ifndef SWIG
/// \name Helper functions
/// Should not be called directly!
//@{
idaman bool ida_export bitrange_t_extract_using_bitrange(const bitrange_t *bm, void *dst, size_t dst_size, const void *src, size_t src_size, bool is_mf);
idaman bool ida_export bitrange_t_inject_using_bitrange(const bitrange_t *bm, void *dst, size_t dst_size, const void *src, size_t src_size, bool is_mf);
//@}
#else
#endif // SWIG
//--------------------------------------------------------------------------
inline bool bitrange_t::extract(
void *dst,
size_t dst_size,
const void *src,
size_t src_size,
bool is_mf) const
{
return bitrange_t_extract_using_bitrange(this, dst, dst_size, src, src_size, is_mf);
}
//--------------------------------------------------------------------------
inline bool bitrange_t::extract(
bytevec_t *dst,
const void *src,
size_t src_size,
bool is_mf) const
{
size_t dst_size = empty() ? src_size : bytesize();
dst->resize(dst_size);
return bitrange_t_extract_using_bitrange(this,
dst->begin(), dst_size,
src, src_size,
is_mf);
}
//--------------------------------------------------------------------------
inline bool bitrange_t::inject(
void *dst,
size_t dst_size,
const void *src,
size_t src_size,
bool is_mf) const
{
return bitrange_t_inject_using_bitrange(this, dst, dst_size, src, src_size, is_mf);
}
//--------------------------------------------------------------------------
inline bool bitrange_t::inject(
void *dst,
size_t dst_size,
const bytevec_t &src,
bool is_mf) const
{
return bitrange_t_inject_using_bitrange(this,
dst, dst_size,
src.begin(), src.size(),
is_mf);
}
#endif // define _BITMASK_HPP

2556
idasdk76/include/bytes.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,173 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef COMPRESS_HPP
#define COMPRESS_HPP
#include <diskio.hpp>
/*! \file compress.hpp
\brief Data compression functions
*/
/// Compress data.
/// This function depends on the value of legacy_idb, so it is not completely
/// thread safe. However, legacy_idb does not change its value.
/// \return \ref PKZ_
idaman THREAD_SAFE int ida_export zip_deflate(
void *ud,
ssize_t (idaapi *file_reader)(void *ud, void *buf, size_t size),
ssize_t (idaapi *file_writer)(void *ud, const void *buf, size_t size));
/// Uncompress data.
/// This function depends on the value of legacy_idb, so it is not completely
/// thread safe. However, legacy_idb does not change its value.
/// \return \ref PKZ_
idaman THREAD_SAFE int ida_export zip_inflate(
void *ud,
ssize_t (idaapi *file_reader)(void *ud, void *buf, size_t size),
ssize_t (idaapi *file_writer)(void *ud, const void *buf, size_t size));
/// Process zip file and enumerate all files stored in it
/// \param zipfile name of zip file
/// \param callback callback for each file. params:
/// - ud: user data
/// - offset: offset in the zip file
/// - method: compression method (\ref compression_methods)
/// - csize: compressed size
/// - ucsize: uncompressed size
/// - attributes: file attributes
/// \param ud user data
/// \return \ref PKZ_
idaman THREAD_SAFE int ida_export process_zipfile(
const char *zipfile,
int (idaapi *callback)(
void *ud,
qoff64_t offset,
int method,
uint64 csize,
uint64 ucsize,
uint32 attributes,
const char *filename),
void *ud = NULL);
/// Process zip file and enumerate all files stored in it
/// \param li input file
/// \param callback callback for each file. params:
/// - ud: user data
/// - offset: offset in the zip file
/// - method: compression method (\ref compression_methods)
/// - csize: compressed size
/// - ucsize: uncompressed size
/// - attributes: file attributes
/// \param ud user data
/// \return \ref PKZ_
idaman THREAD_SAFE int ida_export process_zip_linput(
linput_t *li,
int (idaapi *callback)(
void *ud,
qoff64_t offset,
int method,
uint64 csize,
uint64 ucsize,
uint32 attributes,
const char *filename),
void *ud = NULL);
/// Search for specified entry in zip file, and calls the
/// callback with it, if found.
/// \param zipfile name of zip file
/// \param entry entry in zip file. E.g., "path/to/entry.dat"
/// \param callback callback for each file. params:
/// - ud: user data
/// - offset: offset in the zip file
/// - method: compression method (\ref compression_methods)
/// - csize: compressed size
/// - ucsize: uncompressed size
/// - attributes: file attributes
/// \param ud user data
/// \param case_sensitive should the search be case sensitive?
/// \return \ref PKZ_
idaman THREAD_SAFE int ida_export process_zipfile_entry(
const char *zipfile,
const char *entry,
int (idaapi *callback)(
void *ud,
qoff64_t offset,
int method,
uint64 csize,
uint64 ucsize,
uint32 attributes,
const char *filename),
void *ud = NULL,
bool case_sensitive = true);
/// \defgroup PKZ_ Compression error codes
/// Returned by functions in compress.hpp
//@{
#define PKZ_OK 0
#define PKZ_ERRNO 1
#define PKZ_STREAM_ERROR 2
#define PKZ_DATA_ERROR 3
#define PKZ_MEM_ERROR 4
#define PKZ_BUF_ERROR 5
#define PKZ_VERSION_ERROR 6
#define PKZ_RERR 777 // read error
#define PKZ_WERR 778 // write error
//@}
/// \defgroup compression_methods Compression methods
/// passed as 'method' parameter to callback functions in compress.hpp
//@{
#define STORED 0
#define SHRUNK 1
#define REDUCED1 2
#define REDUCED2 3
#define REDUCED3 4
#define REDUCED4 5
#define IMPLODED 6
#define TOKENIZED 7
#define DEFLATED 8
#define NUM_METHODS 9 /* index of last method + 1 */
//@}
extern bool legacy_idb; ///< for old idb files
/// Upon closing outer linput, perform one of these actions
enum linput_close_code_t
{
LOC_CLOSE, ///< close the inner linput
LOC_UNMAKE, ///< unmake the inner linput
LOC_KEEP, ///< do nothing
};
/// Create a linput to read a compressed input stream
/// \param in linput with compressed data, seeked to the stream beginning
/// \param insize size of compressed data. -1 - unknown
/// \param loc what to do upon closing the resulting linput
/// \return linput that can be used to read uncompressed data.
/// NULL if any error (no more linput descriptors).
idaman THREAD_SAFE linput_t *ida_export create_zip_linput(
linput_t *in,
ssize_t insize=-1,
linput_close_code_t loc=LOC_CLOSE);
#endif

543
idasdk76/include/config.hpp Normal file
View File

@@ -0,0 +1,543 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef _CONFIG_HPP
#define _CONFIG_HPP
//-----------------------------------------------------------------------
/// \defgroup IDPOPT_T Option value types
/// Passed as 'value_type' parameter to ::set_options_t callbacks
//@{
#define IDPOPT_STR 1 ///< string constant (char *)
#define IDPOPT_NUM 2 ///< number (uval_t *)
#define IDPOPT_BIT 3 ///< bit, yes/no (int *)
#define IDPOPT_I64 5 ///< 64bit number (int64 *)
#define IDPOPT_CST 6 ///< lexer (lexer_t*)
///< Custom type, starting with a '{'
///< Values of this type should be handled by
///< ::set_options_t callbacks. E.g.,:
///< \code
///< ERROR_STRINGS =
///< {
///< {0, "Unknown error"},
///< {1, "Missing filename"},
///< {5, "Out-of-memory"}
///< }
///< \endcode
///< For values of this type, the data that will
///< be passed as the callback's 'value' parameter
///< is the lexer instance that is being used
///< to parse the configuration file.
///< You can use \ref parse_json() (see parsejson.hpp)
///< to parse JSON-format data
///< NB: the '{' is already consumed by the parser,
///< so you need to push it again if it's a part of the JSON object
//@}
/// \defgroup IDPOPT_RET Option result codes
/// Predefined return values for ::set_options_t callbacks
//@{
#define IDPOPT_OK NULL ///< ok
#define IDPOPT_BADKEY ((char*)1) ///< illegal keyword
#define IDPOPT_BADTYPE ((char*)2) ///< illegal type of value
#define IDPOPT_BADVALUE ((char*)3) ///< illegal value (bad range, for example)
//@}
/// Callback - called when a config directive is processed in IDA.
/// Also see read_config_file() and processor_t::set_idp_options
/// \param keyword keyword encountered in IDA.CFG/user config file.
/// if NULL, then an interactive dialog form should be displayed
/// \param value_type type of value of the keyword - one of \ref IDPOPT_T
/// \param value pointer to value
/// \param idb_loaded true if the ev_oldfile/ev_newfile events have been generated?
/// \return one of \ref IDPOPT_RET, otherwise a pointer to an error message
typedef const char *(idaapi set_options_t)(
const char *keyword,
int value_type,
const void *value,
bool idb_loaded);
/// \defgroup IDAOPT_PRIO Option priority
/// Specifies the priority of a configuration option. Since options may
/// be specified in different way, and applied in various orders, we need
/// option priorities.
/// Normally the default priority option does not overwrite the existing value
/// whereas the high priority one does.
/// High priority options may be stored in the database to be available
/// in the next session.
//@{
#define IDPOPT_PRI_DEFAULT 1 ///< default priority - taken from config file
#define IDPOPT_PRI_HIGH 2 ///< high priority - received from UI or a script function
//@}
//-------------------------------------------------------------------------
/// Parse the value type for the value token 'value'.
/// This is mostly used for converting from values that a cfgopt_handler_t
/// receives, into data that callbacks
/// - processor_t::set_idp_options
/// - debugger_t::set_dbg_options
/// expect.
///
/// Plugins that wish to use options shouldn't rely on this,
/// and use the cfgopt_t utility instead.
///
/// \param out parsed data
/// \param lx the lexer in use
/// \param value the value token
/// \return true if guessing didn't lead to an error, false otherwise.
/// note that even if 'true' is returned, it doesn't mean the
/// type could be guessed: merely that no syntax error occurred.
class lexer_t;
struct token_t;
idaman bool ida_export parse_config_value(
idc_value_t *out,
lexer_t *lx,
const token_t &value);
//-------------------------------------------------------------------------
typedef const char *(idaapi cfgopt_handler_t)(
lexer_t *lx,
const token_t &keyword,
const token_t &value);
//-------------------------------------------------------------------------
typedef const char *(idaapi cfgopt_handler2_t)(
lexer_t *lx,
const token_t &keyword,
const token_t &value,
int64 param1,
int64 param2);
//-------------------------------------------------------------------------
typedef const char *(idaapi cfgopt_handler3_t)(
lexer_t *lx,
const token_t &keyword,
const token_t &value,
int64 param1,
int64 param2,
void *obj);
//-----------------------------------------------------------------------
/// used by cfgopt_t. You shouldn't have to deal with those directly.
#define IDPOPT_NUM_INT (0)
#define IDPOPT_NUM_CHAR (1 << 24)
#define IDPOPT_NUM_SHORT (2 << 24)
#define IDPOPT_NUM_RANGE (1 << 26)
#define IDPOPT_NUM_UNS (1 << 27)
#define IDPOPT_BIT_UINT 0
#define IDPOPT_BIT_UCHAR (1 << 24)
#define IDPOPT_BIT_USHORT (2 << 24)
#define IDPOPT_BIT_BOOL (3 << 24)
#define IDPOPT_STR_QSTRING (1 << 24)
#define IDPOPT_STR_LONG (1 << 25)
#define IDPOPT_I64_RANGES (1 << 24)
#define IDPOPT_I64_UNS (1 << 25)
#define IDPOPT_CST_PARAMS (1 << 24)
#define IDPOPT_MBROFF (1 << 18)
//-------------------------------------------------------------------------
struct cfgopt_t;
idaman const char *ida_export cfgopt_t__apply(
const cfgopt_t *_this,
int vtype,
const void *vdata);
idaman const char *ida_export cfgopt_t__apply2(
const cfgopt_t *_this,
int vtype,
const void *vdata,
void *obj);
//-------------------------------------------------------------------------
// cfgopt_t objects are suitable for being statically initialized, and
// passed to 'read_config_file'.
//
// E.g.,
// ---
// static const cfgopt_t g_opts[] =
// {
// cfgopt_t("AUTO_UNDEFINE", &auto_undefine, -1, 1),
// cfgopt_t("NOVICE", &novice, true),
// cfgopt_t("EDITOR", editor_buf, sizeof(editor_buf)),
// cfgopt_t("SCREEN_PALETTE", set_screen_palette), // specific handler for SCREEN_PALETTE
// };
//
// ...
//
// read_config_file("myfile", g_opts, qnumber(g_opts), other_handler)
// ---
//
// NOTES:
// * so-called 'long' strings (the default) can span on multiple lines,
// and are terminated by a ';'
struct cfgopt_t
{
const char *name;
union
{
void *ptr;
size_t mbroff; // offset of a structure member
cfgopt_handler_t *hnd; // to avoid reinterpret_cast and gcc's error:
cfgopt_handler2_t *hnd2; // "a reinterpret_cast is not a constant expression"
cfgopt_handler3_t *hnd3; //
};
int flags;
struct num_range_t
{
constexpr num_range_t(int64 _min, int64 _max) : minval(_min), maxval(_max) {}
int64 minval;
int64 maxval;
};
struct params_t
{
constexpr params_t(int64 _p1, int64 _p2) : p1(_p1), p2(_p2) {}
int64 p1;
int64 p2;
};
union
{
size_t buf_size;
num_range_t num_range;
uint32 bit_flags;
params_t params;
void *mbroff_obj;
};
// IDPOPT_STR
constexpr cfgopt_t(const char *_n, char *_p, size_t _sz, bool _long = true)
: name(_n), ptr(_p), flags(IDPOPT_STR | (_long ? IDPOPT_STR_LONG : 0)), buf_size(_sz)
{}
constexpr cfgopt_t(const char *_n, qstring *_p, bool _long = true)
: name(_n), ptr(_p), flags(IDPOPT_STR | IDPOPT_STR_QSTRING | (_long ? IDPOPT_STR_LONG : 0)), buf_size(0)
{}
// IDPOPT_NUM
constexpr cfgopt_t(const char *_n, int *_p)
: name(_n), ptr(_p), flags(IDPOPT_NUM), buf_size(0) {}
constexpr cfgopt_t(const char *_n, uint *_p)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS), buf_size(0) {}
constexpr cfgopt_t(const char *_n, char *_p)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_CHAR), buf_size(0) {}
constexpr cfgopt_t(const char *_n, uchar *_p)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_CHAR), buf_size(0) {}
constexpr cfgopt_t(const char *_n, short *_p)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_SHORT), buf_size(0) {}
constexpr cfgopt_t(const char *_n, ushort *_p)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_SHORT), buf_size(0) {}
// IDPOPT_NUM + ranges
constexpr cfgopt_t(const char *_n, int *_p, int _min, int _max)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_RANGE), num_range(_min, _max) {}
constexpr cfgopt_t(const char *_n, uint *_p, uint _min, uint _max)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_RANGE), num_range(_min, _max) {}
constexpr cfgopt_t(const char *_n, char *_p, char _min, char _max)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_CHAR | IDPOPT_NUM_RANGE), num_range(_min, _max) {}
constexpr cfgopt_t(const char *_n, uchar *_p, uchar _min, uchar _max)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_CHAR | IDPOPT_NUM_RANGE), num_range(_min, _max) {}
constexpr cfgopt_t(const char *_n, short *_p, short _min, short _max)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_RANGE | IDPOPT_NUM_SHORT), num_range(_min, _max) {}
constexpr cfgopt_t(const char *_n, ushort *_p, ushort _min, ushort _max)
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_RANGE | IDPOPT_NUM_SHORT), num_range(_min, _max) {}
// IDPOPT_BIT
constexpr cfgopt_t(const char *_n, bool *_p, bool _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_BOOL), bit_flags(_flags) {}
constexpr cfgopt_t(const char *_n, uchar *_p, uchar _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_UCHAR), bit_flags(_flags) {}
constexpr cfgopt_t(const char *_n, ushort *_p, ushort _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_USHORT), bit_flags(_flags) {}
constexpr cfgopt_t(const char *_n, uint32 *_p, uint32 _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT), bit_flags(_flags) {}
// IDPOPT_I64
constexpr cfgopt_t(const char *_n, int64 *_p) : name(_n), ptr(_p), flags(IDPOPT_I64), buf_size(0) {}
constexpr cfgopt_t(const char *_n, uint64 *_p) : name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_NUM_UNS), buf_size(0) {}
// IDPOPT_I64 + ranges
constexpr cfgopt_t(const char *_n, int64 *_p, int64 _min, int64 _max)
: name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_I64_RANGES), num_range(_min, _max) {}
constexpr cfgopt_t(const char *_n, uint64 *_p, uint64 _min, uint64 _max)
: name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_I64_UNS | IDPOPT_I64_RANGES), num_range(int64(_min), int64(_max)) {}
// IDPOPT_CST
constexpr cfgopt_t(const char *_n, cfgopt_handler_t *_p)
: name(_n), hnd(_p), flags(IDPOPT_CST), buf_size(0) {}
// IDPOPT_CST + params
constexpr cfgopt_t(const char *_n, cfgopt_handler2_t *_p, int64 _p1=0, int64 _p2=0)
: name(_n), hnd2(_p), flags(IDPOPT_CST | IDPOPT_CST_PARAMS), params(_p1, _p2) {}
// configuration option based on the offset of a structure member
// IDPOPT_STR
template<class T>
constexpr cfgopt_t(const char *_n, qstring T:: *, size_t _mbroff, bool _long = true)
: name(_n),
mbroff(_mbroff),
flags(IDPOPT_MBROFF | IDPOPT_STR | IDPOPT_STR_QSTRING | (_long ? IDPOPT_STR_LONG : 0)),
buf_size(0)
{}
#define CFGOPT_QS(nm, cfgt, cfgm, _long) \
cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), _long)
#define CFGOPT_INNER_QS(nm, cfgt, cfgm, mt, mf, _long) \
cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), _long)
// IDPOPT_NUM
#define CTR_CFGOPT(ctrtype, ctrflags) \
template<class T> \
constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff) \
: name(_n), \
mbroff(_mbroff), \
flags(IDPOPT_MBROFF|IDPOPT_NUM|ctrflags), \
buf_size(0) \
{}
CTR_CFGOPT(int, 0)
CTR_CFGOPT(uint, IDPOPT_NUM_UNS)
CTR_CFGOPT(char, IDPOPT_NUM_CHAR)
CTR_CFGOPT(uchar, IDPOPT_NUM_UNS|IDPOPT_NUM_CHAR)
CTR_CFGOPT(short, IDPOPT_NUM_SHORT)
CTR_CFGOPT(ushort, IDPOPT_NUM_SHORT|IDPOPT_NUM_UNS)
#undef CTR_CFGOPT
#define CFGOPT_N(nm, cfgt, cfgm) \
cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm))
#define CFGOPT_INNER_N(nm, cfgt, cfgm, mt, mf) \
cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf))
// IDPOPT_NUM + ranges
#define CTR_CFGOPT(ctrtype, ctrflags) \
template<class T> \
constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff, int64 _min, int64 _max) \
: name(_n), \
mbroff(_mbroff), \
flags(IDPOPT_MBROFF|IDPOPT_NUM|IDPOPT_NUM_RANGE|ctrflags), \
num_range(_min, _max) \
{}
CTR_CFGOPT(int, 0)
CTR_CFGOPT(uint, IDPOPT_NUM_UNS)
CTR_CFGOPT(char, IDPOPT_NUM_CHAR)
CTR_CFGOPT(uchar, IDPOPT_NUM_UNS|IDPOPT_NUM_CHAR)
CTR_CFGOPT(short, IDPOPT_NUM_SHORT)
CTR_CFGOPT(ushort, IDPOPT_NUM_SHORT|IDPOPT_NUM_UNS)
#undef CTR_CFGOPT
#define CFGOPT_R(nm, cfgt, cfgm, min, max) \
cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), min, max)
#define CFGOPT_INNER_R(nm, cfgt, cfgm, mt, mf, min, max) \
cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), min, max)
// IDPOPT_BIT
#define CTR_CFGOPT(ctrtype, ctrflags) \
template<class T> \
constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff, ctrtype _flags) \
: name(_n), \
mbroff(_mbroff), \
flags(IDPOPT_MBROFF|IDPOPT_BIT|ctrflags), \
bit_flags(_flags) \
{}
CTR_CFGOPT(bool, IDPOPT_BIT_BOOL);
CTR_CFGOPT(uchar, IDPOPT_BIT_UCHAR);
CTR_CFGOPT(ushort, IDPOPT_BIT_USHORT);
CTR_CFGOPT(uint32, 0);
#undef CTR_CFGOPT
#define CFGOPT_B(nm, cfgt, cfgm, _flags) \
cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), _flags)
#define CFGOPT_INNER_B(nm, cfgt, cfgm, mt, mf, _flags) \
cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), _flags)
// IDPOPT_I64
template<class T>
constexpr cfgopt_t(const char *_n, int64 T:: *, size_t _mbroff)
: name(_n),
mbroff(_mbroff),
flags(IDPOPT_MBROFF|IDPOPT_I64),
buf_size(0)
{}
template<class T>
constexpr cfgopt_t(const char *_n, uint64 T:: *, size_t _mbroff)
: name(_n),
mbroff(_mbroff),
flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_NUM_UNS),
buf_size(0)
{}
// IDPOPT_I64 + ranges
template<class T>
constexpr cfgopt_t(const char *_n, int64 T:: *, size_t _mbroff, int64 _min, int64 _max)
: name(_n),
mbroff(_mbroff),
flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_I64_RANGES),
num_range(_min, _max)
{}
template<class T>
constexpr cfgopt_t(const char *_n, uint64 T:: *, size_t _mbroff, uint64 _min, uint64 _max)
: name(_n),
mbroff(_mbroff),
flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_I64_UNS|IDPOPT_I64_RANGES),
num_range(int64(_min), int64(_max))
{}
// IDPOPT_CST + params
constexpr cfgopt_t(const char *_n, cfgopt_handler3_t *_p, int64 _p1=0, int64 _p2=0)
: name(_n), hnd3(_p), flags(IDPOPT_MBROFF|IDPOPT_CST), params(_p1, _p2) {}
int type() const { return flags & 0xf; }
int qualifier() const { return flags & 0xf000000; }
const char *apply(int vtype, const void *vdata, void *obj=nullptr) const
{
return cfgopt_t__apply2(this, vtype, vdata, obj);
}
};
/// Parse the input, and apply options.
///
/// \param input input file name, or string
/// \param is_file is input a string, or a file name
/// \param opts options destcriptions
/// \param nopts the number of entries present in the 'opts' array
/// \param defhdlr a handler to be called, if a directive couldn't be found in 'opts'
/// \param defines a list of preprocessor identifiers to define (so it is
/// possible to use #ifdef checks in the file.)
/// NB: the actual identifier defined by the parser will be
/// surrounded with double underscores (e.g., passing 'FOO'
/// will result in '__FOO__' being defined)
/// Additionally, the parser will also define a similar macro
/// with the current processor name (e.g., __ARM__)
/// \param ndefines the number of defines in the list
/// \param obj see cfgopt_t constructor based on the offset of a structure member
/// \return true if parsing finished without errors, false if there was a
/// syntax error, callback returned an error, or no file was found
/// at all.
idaman bool ida_export read_config(
const char *input,
bool is_file,
const cfgopt_t opts[],
size_t nopts,
cfgopt_handler_t *defhdlr = NULL,
const char *const *defines = NULL,
size_t ndefines = 0);
idaman bool ida_export read_config2(
const char *input,
bool is_file,
const cfgopt_t opts[],
size_t nopts,
cfgopt_handler_t *defhdlr = nullptr,
const char *const *defines = nullptr,
size_t ndefines = 0,
void *obj = nullptr);
inline bool read_config_file2(
const char *filename,
const cfgopt_t opts[],
size_t nopts,
cfgopt_handler_t *defhdlr = nullptr,
const char *const *defines = nullptr,
size_t ndefines = 0,
void *obj = nullptr)
{
return read_config2(filename, true, opts, nopts, defhdlr, defines, ndefines, obj);
}
/// Search for all IDA system files with the given name.
/// This function will search, in that order, for the following files:
/// -# %IDADIR%/cfg/<file>
/// -# for each directory 'ONEDIR' in %IDAUSR%: %ONEDIR%/cfg/<file>
///
/// For each directive in each of those files, the same processing as
/// that of read_config will be performed.
inline bool read_config_file(
const char *filename,
const cfgopt_t opts[],
size_t nopts,
cfgopt_handler_t *defhdlr = NULL,
const char *const *defines = NULL,
size_t ndefines = 0)
{
return read_config(filename, true, opts, nopts, defhdlr, defines, ndefines);
}
/// For each directive in 'string', the same processing as that of
/// read_config will be performed.
inline bool read_config_string(
const char *string,
const cfgopt_t opts[],
size_t nopts,
cfgopt_handler_t *defhdlr = NULL,
const char *const *defines = NULL,
size_t ndefines = 0)
{
return read_config(string, false, opts, nopts, defhdlr, defines, ndefines);
}
/// Process one or more config directive(s).
/// \param directive the directives to process
/// \param priority priority \ref IDPOPT_RET
/// In the case of errors this function displays a message and exits.
idaman void ida_export process_config_directive(
const char *directive,
int priority=IDPOPT_PRI_HIGH);
/// Register array of config options.
/// This function can be used by a plugin to register the config options.
/// After registering an option, it becomes usable by the
/// process_config_directive() function.
/// \param opts array of config options
/// \param nopts number of options to install. 0 means uninstall
/// \param cb callback that will be invoked upon changing a config option
/// \param obj see cfgopt_t constructor based on the offset of a structure member
/// \return success
typedef void idaapi config_changed_cb_t(const cfgopt_t &opt, int vtype, const void *vdata);
idaman bool ida_export register_cfgopts(
const cfgopt_t opts[],
size_t nopts,
config_changed_cb_t cb=nullptr,
void *obj=nullptr);
/// Get one of config parameters defined by CC_PARMS in ida.cfg.
/// All parameters for all compilers are stored in local map during last read
/// of ida.cfg - this function just returns previously stored parameter value for
/// given compiler (NULL if no such parameter)
idaman const char *ida_export cfg_get_cc_parm(comp_t compid, const char *name);
/// Get header path config parameter from ida.cfg.
/// Also see cfg_get_cc_parm()
inline const char *cfg_get_cc_header_path(comp_t compid)
{
return cfg_get_cc_parm(compid, "HEADER_PATH");
}
/// Get predefined macros config parameter from ida.cfg.
/// Also see cfg_get_cc_parm()
inline const char *cfg_get_cc_predefined_macros(comp_t compid)
{
return cfg_get_cc_parm(compid, "PREDEFINED_MACROS");
}
#endif // _CONFIG_HPP

2625
idasdk76/include/dbg.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,269 @@
/*
* CPP/D/Swift Demangler.
* Copyright (c) 2000-2018 by Iouri Kharon.
* E-mail: yjh@styx.cabel.net
*
* ALL RIGHTS RESERVED.
*
*/
#ifndef _DEMANGLE_HPP
#define _DEMANGLE_HPP
// int32 result code
#define ME_INTERR -1 // Internal error
#define ME_PARAMERR -2 // Input parameters are wrong
#define ME_ILLSTR -3 // Incorrectly mangled name
#define ME_SMALLANS -4 // Output buffer is too small
// This code is possible only with the 'old' calling
// form. With the new calling form the output buffer
// will have '...' as the last characters and the
// result code or'ed with the sign bit
#define ME_FRAME -5 // Partial demanging is possible (the input name has
// unrecognized suffix)
#define ME_NOCOMP -6 // Could not determine the compiler
#define ME_ERRAUTO -7 // Specified compiler is impossible for the input name
#define ME_NOHASHMEM -8 // Out of internal indexes-most likely bad input name
#define ME_NOSTRMEM -9 // Out of internal buffers (can't be!:)
#define ME_NOERROR_LIMIT -10 // Lowest error number. Lower values
// signal about the truncated output name
// (the output buffer is too small)
#define M_PRCMSK 0x0000000F // If = 0, then data
#define MT_DEFAULT 0x00000001 // 1 - default (for watcom/gnu only this)
#define MT_CDECL 0x00000002 // 2 - __cdecl
#define MT_PASCAL 0x00000003 // 3 - __pascal
#define MT_STDCALL 0x00000004 // 4 - __stdcall
#define MT_FASTCALL 0x00000005 // 5 - __fastcall
#define MT_THISCALL 0x00000006 // 6 - __thiscall [ms & bc => pragma only]
#define MT_FORTRAN 0x00000007 // 7 - __fortran
#define MT_SYSCALL 0x00000008 // 8 - __syscall [without ms]
#define MT_INTERRUPT 0x00000009 // 9 - __interrupt (only with __cdecl!)
#define MT_MSFASTCALL 0x0000000A // A - __msfastcall (bc)
#define MT_CLRCALL 0x0000000B // B - __clrcall (vc7)
#define MT_DMDCALL 0x0000000C // C - __dcall (dm D language abi)
#define MT_VECTORCALL 0x0000000D // D - __vectorcall (vc13)
#define MT_REGCALL 0x0000000E // E - __regcall (icl, clang)
// reserved
#define MT_LOCALNAME 0x0000000F // f - might be function or data. Currently
// is used only for bc - as the
// identifier for local pascal labels
#define M_SAVEREGS 0x00000010 // For functions with "__saveregs"
#define M_CLASS 0x000000E0 // 0 - no keyword (not a member field)
#define MT_PUBLIC 0x00000020 // 1 - public
#define MT_PRIVATE 0x00000040 // 2 - private
#define MT_PROTECT 0x00000060 // 3 - protected
#define MT_MEMBER 0x00000080 // 4 - undetermined (bc/wat/gcc)
#define MT_VTABLE 0x000000A0 // 5 - vtable (bc/gnu)
#define MT_RTTI 0x000000C0 // 6 - typeinfo table (gcc3), witness table (Swift)
// reserved
#define M_PARMSK 0x0000FF00 // Parameter number mask (excluding ellipsis)
// 255 - >= 255
#define MT_PARSHF 8 // shift to PARMSK
#define MT_PARMAX 0xFF // Number limiter
// ATT: when CC is __vectorcall and mode is 'C'
// real argscount is unknown. This number is
// total sizeof of all arguments divided to
// sizeof of defptr
#define M_ELLIPSIS 0x00010000 // The function _certainly_ has '...'
#define MT_VOIDARG 0x0001FF00 // If = 0, the func(void), i.e. no parameters
#define M_STATIC 0x00020000 // static
// gcc3 - static data in a function
// might be encountered in object files and
// (possibly) in binaries with debug info
#define M_VIRTUAL 0x00040000 // virtual
// NOTE: for (D) not virtual -- this (counted!)
#define M_AUTOCRT 0x00080000 // Most likely "autogenerated" function (data)
// NOTE: +M_STATIC => "__linkproc__" (bc)
#define M_TYPMASK 0x00700000 // Special functions (0-regular function)
#define MT_OPERAT 0x00100000 // 1 - operator
#define MT_CONSTR 0x00200000 // 2 - constructor
#define MT_DESTR 0x00300000 // 3 - destructor
#define MT_CASTING 0x00400000 // 4 - type conversion
#define MT_CLRCDTOR 0x00500000 // 5 - delphi2010 CLR ctor/dtor for packages
// reserved
#define M_TRUNCATE 0x00800000 // Name was truncated by the compiler (bc/va)
#define M_THUNK 0x01000000 // [thunk]:
#define M_ANONNSP 0x02000000 // ms => Anonymous Namespace for field
// gc3 => Item placed in Anonymous namespace
// wat => anonymous_enum
// bc => + TMPLNAM = PascalTemplate (for DCC)
// If separate - "automatic" except_t
// from CBuilder for "external" variables
// or a template for global object
// constructor/destructor tables (for CBuilder
// as well)
#define M_TMPLNAM 0x04000000 // ms => template name (?)
// wat =>
// bc => template name => its description table
// gc3 => any template funciton/data
#define M_DBGNAME 0x08000000 // ms => CV:
// wat => xxxx: (T?xxxx-form)
// bc => old pascal format (capitalized)
// gc3 => unicode symbols or 'vendor-extension'
// qualifiers are present
#define M_COMPILER 0x70000000 // Compiler mask (0-unknown)
#define MT_MSCOMP 0x10000000 // 1 - microsoft/symantec
#define MT_BORLAN 0x20000000 // 2 - borland
#define MT_WATCOM 0x30000000 // 3 - watcom
#define MT_OTHER 0x40000000 // 4 - digital mars D language (start: _D)
// - apple Swift language (start: [_]_T)
// !!! The following definitions must be last and in this order!
#define MT_GNU 0x50000000 // 5 - GNU - (over VA for autodetection)
#define MT_GCC3 0x60000000 // 6 - gcc-v3
// In the short form this answer is possible
// for GNU/VA as well, but gcc3 can be
// explicitly requested only with it.
// Autodetection works but not very reliable.
#define MT_VISAGE 0x70000000 // 7 - Visual Age - never autodetected
// In the short form this answer means VA
// or GNU. In the automatic mode GNU will
// be used!
//---------------------------------------------------------------------------
// Flags to inhibit different parts of the demangled name
#define MNG_PTRMSK 0x7 // Memory model mask
// DO NOT change order in this group (PtrType)
#define MNG_DEFNEAR 0x0 // inhibit near, display everything else
#define MNG_DEFNEARANY 0x1 // inhibit near/__ptr64, display everything else
#define MNG_DEFFAR 0x2 // inhibit far, display everything else
#define MNG_NOPTRTYP16 0x3 // inhibit everything (disables vc7-extensions)
#define MNG_DEFHUGE 0x4 // inhibit huge, display everything else
#define MNG_DEFPTR64 0x5 // inhibit __pt64, display everything else
// ATT: in 64bit must be + MNG_NOTYPE|MNG_NOCALLC
#define MNG_DEFNONE 0x6 // display everything
#define MNG_NOPTRTYP 0x7 // inhibit everything
//
#define MNG_NODEFINIT 0x00000008 // Inhibit everything except the main name
// This flag is not recommended
// for __fastcall/__stdcall GCC3 names
// because there is a high probablity of
// incorrect demangling. Use it only when
// you are sure that the input is a
// cygwin/mingw function name
//
#define MNG_NOUNDERSCORE 0x00000010 // Inhibit underscores in __ccall, __pascal... +
#define MNG_NOTYPE 0x00000020 // Inhibit callc&based
#define MNG_NORETTYPE 0x00000040 // Inhibit return type of functions
#define MNG_NOBASEDT 0x00000080 // Inhibit base types
// NOTE: also inhibits "__linkproc__"
// NOTE: -"- 'implicit self types' (Swift)
#define MNG_NOCALLC 0x00000100 // Inhibit __pascal/__ccall/etc
// NOTE: also inhibits "extern (cc)" (D)
#define MNG_NOPOSTFC 0x00000200 // Inhibit postfix const
#define MNG_NOSCTYP 0x00000400 // Inhibit public/private/protected
// NOTE: also inhibits in/out/lazy for args (D)
// NOTE: -"- dynamic/super/override/... (Swift)
#define MNG_NOTHROW 0x00000800 // Inhibit throw description
// NOTE: also inhibits all funcattr (D)
#define MNG_NOSTVIR 0x00001000 // Inhibit "static" & "virtual"
// NOTE: also inhibits (D) top-level procs (<=)
#define MNG_NOECSU 0x00002000 // Inhibit class/struct/union/enum[/D:typedef]
#define MNG_NOCSVOL 0x00004000 // Inhibit const/volatile/restrict
// NOTE: also inhibits __unaligned (vc)
// NOTE: also inhibits transaction_safe(gcc)
// NOTE: also inhibits shared/immutable (D)
// NOTE: also inhibits prefix/postfix/infix/inout (Swift)
#define MNG_NOCLOSUR 0x00008000 // Inhibit __closure for borland
// 'reabstract thunk' description (Swift)
#define MNG_NOUNALG 0x00010000 // Inhibit __unaligned (see NOCSVOL)
// NOTE: also inhibit transaction_safe (see NOCSVOL)
#define MNG_NOMANAGE 0x00020000 // Inhibit __pin/__box/__gc for ms(.net)
// NOTE: also inhibit archetype/witness (Swift)
// NOTE: also ingibit [abi:xxxx] (gcc3)
#define MNG_NOMODULE 0x00040000 // Inhibit module names (Swift)
// 0x00080000
//
#define MNG_SHORT_S 0x00100000 // signed (int) is displayed as s(int)
#define MNG_SHORT_U 0x00200000 // unsigned (int) is displayed as u(int)
#define MNG_ZPT_SPACE 0x00400000 // Display space after comma in the arglist
// NOTE: also spaces in name:type pair (Swift)
// and around Swift return clause ->
#define MNG_DROP_IMP 0x00800000 // Inhibit __declspec(dllimport)
//
// 0x01000000
#define MNG_IGN_ANYWAY 0x02000000 // Ingore '_nn' at the end of name
#define MNG_IGN_JMP 0x04000000 // Ingore 'j_' at the beginning of name
#define MNG_MOVE_JMP 0x08000000 // Move 'j_' prefix to the demangled name
// If both MNG_IGN_JMP and MNG_MOVE_JMP
// are set then move the prefix only if
// the name was not truncated
//
#define MNG_COMPILER_MSK 0x70000000 // Compiler mask (0-autodetect)
#define MNG_SHORT_FORM (MNG_NOTYPE|MNG_NORETTYPE|MNG_NOPOSTFC|MNG_NOPTRTYP \
| MNG_NOSCTYP|MNG_NOTHROW|MNG_NOSTVIR|MNG_NOECSU|MNG_NOCLOSUR \
| MNG_SHORT_U|MNG_DROP_IMP|MNG_NOUNALG|MNG_NOMANAGE \
| MNG_IGN_JMP|MNG_MOVE_JMP|MNG_IGN_ANYWAY)
#define MNG_LONG_FORM (MNG_ZPT_SPACE | MNG_IGN_JMP | MNG_IGN_ANYWAY | MNG_NOPTRTYP)
// The description of the following symbol is in the notes
#define MNG_CALC_VALID (MNG_COMPILER_MSK|MNG_IGN_JMP|MNG_IGN_ANYWAY)
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef H2ASH
#if !defined(NO_OBSOLETE_FUNCS) || defined(__DEFINE_DEMANGLE__)
typedef int32 ida_export demangler_t(
char *answer,
uint answer_length,
const char *str,
uint32 disable_mask);
idaman demangler_t demangle;
#endif
// If answer_length == 0 then no demangling is performed neither. The function
// will check if demangling is possible and what compiler is used to mangle
// the name. If the name cannot be demangled then the function will return 0.
// NOTE: the answer MT_MSCOMP+1 means __msfastcall
// (or borland class name with "_4" suffix) and the demangling is possible
// either as MS (__fastcall) or as bc (__msfastcall)
// NOTE: the answer MT_GCC3+1 means POSSIBLE mingw/cygwin with
// __stdcall/__fastcall but it might also mean ms-stdcall.
// In essense it means that the demangler cannot determine the compiler
// precisely.
// It also means that the demangling is possible in the gcc3 mode
// ONLY when the compiler is explicitly set to gcc3 and MNG_NODEFINIT
// bit is not set.
// If answer == NULL then the demangler will return check if the demangling
// is possible and only return the flags.
// In this case answer_length should be enough to hold the demangled name.
// NOTE: If int32(answer_length) < 0 then the demangler will calcuate the
// the number of purged bytes for the given name. In this case
// disable_mask may contain only bits included in MNG_CALC_VALID,
// and -answer_length must be equal to the register size (e.g.
// sizeof(uint16/uint32/uint64)). The value of the register size
// is used to check the numeric value in the ms stdcall/fastcall
// encoding (it is used in the gcc mode too).
// if return value <= 0 - no purged bytes or valid information.
// If (value & 1) != 0 - ms stdcall (definite npurged is value-1.
// If (value & 1) == 0 - 'encoded' counter (not implemented yet).
// If answer != NULL (and answer_length != 0) - perform demangling and fill out.
// NOTE: if int32(answer_length) < 0 then the buffer size will be -answer_length
// but 'answer' is interpreted not as a pointer to the output buffer but
// as a pointer to pointer to the output buffer (char**).
// In this case if the function succeeds,a pointer to the answer end
// will be returned in the pointer (like stpcpy). In this form 'answer'
// cannot be NULL and *(char**)answer cannot be NULL.
// answer_length must be greater than 9 for the 'truncated' answer.
typedef int mangled_name_type_t;
const mangled_name_type_t MANGLED_CODE = 0;
const mangled_name_type_t MANGLED_DATA = 1;
const mangled_name_type_t MANGLED_UNKNOWN = 2;
idaman mangled_name_type_t ida_export get_mangled_name_type(const char *name);
#endif // H2ASH
#endif // _DEMANGLE_HPP

View File

@@ -0,0 +1,468 @@
/*
This class is used to organize a directory tree on top of any
collection that allows for accessing its elements by an id (inode).
No requirements are imposed on the inodes apart from the forbidden
value -1 (it is used ot denote a bad inode).
The dirspec_t class is used to specialize the dirtree.
It can be used to introduce a directory structure for:
- local types
- structs
- enums
- functions
- names
- etc
*/
#ifndef DIRTREE_HPP
#define DIRTREE_HPP
//------------------------------------------------------------------------
typedef qvector<inode_t> inodevec_t; // sequence of inodes
/// Directory indexes are simple numbers like 0,1,2,3...
/// They are independent of inode numbers.
/// The root directory always exists and has the index 0 (\ref direntry_t::ROOTIDX).
typedef uval_t diridx_t;
typedef qvector<diridx_t> dirvec_t; // sequence of directory indexes
// Directory entry: either a file or directory
// We use PACKED to save memory, without it we would spend 64 bits instead of
// 8 bits to store a 1-bit value on ida64.
#pragma pack(push, 1)
struct PACKED direntry_t
{
uval_t idx; // diridx_t or inode_t
bool isdir;
static const uval_t BADIDX = uval_t(-1);
static const uval_t ROOTIDX = 0;
direntry_t(uval_t i=BADIDX, bool d=false) : idx(i), isdir(d) {}
bool valid() const { return idx != BADIDX; }
bool operator==(const direntry_t &r) const
{
return idx == r.idx && isdir == r.isdir;
}
bool operator!=(const direntry_t &r) const
{
return !(*this == r);
}
bool operator<(const direntry_t &r) const
{
if ( !isdir && r.isdir )
return true;
if ( isdir && !r.isdir )
return false;
return idx < r.idx;
}
};
#pragma pack(pop)
DECLARE_TYPE_AS_MOVABLE(direntry_t);
typedef qvector<direntry_t> direntry_vec_t;
//------------------------------------------------------------------------
/// Directory tree specialization. This is an abstract base class that
/// represents 'file items' of our directory structure.
struct dirspec_t
{
uint32 flags;
enum
{
DSF_INODE_EA = 0x01, // inode is EA, will be handled during segment moving
DSF_PRIVRANGE = 0x02, // inode is tid_t, structure or enum id, will be handled during segment moving
};
// netnode name to load/save directory tree
// if not specified the loading/storing operations are not supported
qstring nodename;
dirspec_t(const char *nm=nullptr, uint32 f=0) : flags(f), nodename(nm) {}
virtual ~dirspec_t() {}
// get the entry name. for example, the structure name
// out may be nullptr; in this case get_name can be used to validate an inode.
// return false if the entry does not exist.
virtual bool get_name(qstring *out, inode_t inode) = 0;
// get the entry inode. for example, the structure id
virtual inode_t get_inode(diridx_t diridx, const char *name) = 0;
// print additional attributes of the entry. for example, is union? is mapped?
virtual qstring get_attrs(inode_t inode) const = 0;
// rename the entry
virtual bool rename_inode(inode_t inode, const char *newname) = 0;
// event: unlinked an inode
virtual void unlink_inode(inode_t) {}
};
//------------------------------------------------------------------------
/// Position in the directory tree
struct dirtree_cursor_t
{
diridx_t parent;
size_t rank; // item rank: index into parent.subdirs or parent.inodes
// if the index value is greated than parent.subdirs.size()
// then it points to parent.inodes (after -= parent.subdirs.size())
dirtree_cursor_t(diridx_t _parent=direntry_t::BADIDX, size_t _rank=size_t(-1))
: parent(_parent), rank(_rank) {}
bool valid() const { return parent != direntry_t::BADIDX || rank == 0; }
bool is_root_cursor() const { return parent == direntry_t::BADIDX && rank == 0; }
void set_root_cursor(void) { parent = direntry_t::BADIDX; rank = 0; }
static dirtree_cursor_t root_cursor()
{
dirtree_cursor_t c;
c.set_root_cursor();
return c;
}
DECLARE_COMPARISONS(dirtree_cursor_t)
{
if ( parent < r.parent ) return -1;
if ( parent > r.parent ) return 1;
if ( rank < r.rank ) return -1;
if ( rank > r.rank ) return 1;
return 0;
}
};
DECLARE_TYPE_AS_MOVABLE(dirtree_cursor_t);
typedef qvector<dirtree_cursor_t> dirtree_cursor_vec_t;
//-------------------------------------------------------------------------
struct dirtree_selection_t : public dirtree_cursor_vec_t {};
//------------------------------------------------------------------------
/// Helper class to iterate over files
struct dirtree_iterator_t
{
qstring pattern;
dirtree_cursor_t cursor;
};
//------------------------------------------------------------------------
// Directory tree: error codes
enum dterr_t
{
DTE_OK, // ok
DTE_ALREADY_EXISTS, // item already exists
DTE_NOT_FOUND, // item not found
DTE_NOT_DIRECTORY, // item is not a directory
DTE_NOT_EMPTY, // directory is not empty
DTE_BAD_PATH, // invalid path
DTE_CANT_RENAME, // failed to rename an item
DTE_OWN_CHILD, // moving inside subdirectory of itself
DTE_MAX_DIR, // maximum directory count achieved
DTE_LAST,
};
class dirtree_t;
class dirtree_impl_t;
struct segm_move_infos_t;
//------------------------------------------------------------------------
// internal functions; use dirtree_t members instead
#ifndef SWIG
idaman dirtree_impl_t *ida_export create_dirtree(dirtree_t *dt, dirspec_t *ds);
idaman void ida_export delete_dirtree(dirtree_impl_t *d);
idaman bool ida_export load_dirtree(dirtree_impl_t *d);
idaman bool ida_export save_dirtree(dirtree_impl_t *d);
void reset_dirtree(dirtree_impl_t *d);
idaman const char *ida_export dirtree_errstr(dterr_t err);
idaman dterr_t ida_export dirtree_chdir(dirtree_impl_t *d, const char *path);
idaman void ida_export dirtree_getcwd(qstring *out, const dirtree_impl_t *d);
idaman void ida_export dirtree_resolve_path(direntry_t *de, const dirtree_impl_t *d, const char *path);
idaman void ida_export dirtree_resolve_cursor(direntry_t *de, const dirtree_impl_t *d, const dirtree_cursor_t &cursor);
idaman bool ida_export dirtree_get_entry_name(qstring *out, const dirtree_impl_t *d, const direntry_t &de);
idaman void ida_export dirtree_get_entry_attrs(qstring *out, const dirtree_impl_t *d, const direntry_t &de);
idaman ssize_t ida_export dirtree_get_dir_size(dirtree_impl_t *d, diridx_t diridx);
idaman bool ida_export dirtree_findfirst(dirtree_impl_t *d, dirtree_iterator_t *ff, const char *pattern);
idaman bool ida_export dirtree_findnext(dirtree_impl_t *d, dirtree_iterator_t *ff);
idaman bool ida_export dirtree_get_abspath_by_cursor(qstring *out, const dirtree_impl_t *d, const dirtree_cursor_t &cursor);
idaman bool ida_export dirtree_get_abspath_by_relpath(qstring *out, const dirtree_impl_t *d, const char *relpath);
idaman dterr_t ida_export dirtree_mkdir(dirtree_impl_t *d, const char *path);
idaman dterr_t ida_export dirtree_rmdir(dirtree_impl_t *d, const char *path);
idaman dterr_t ida_export dirtree_link(dirtree_impl_t *d, const char *path, bool do_link);
idaman dterr_t ida_export dirtree_link_inode(dirtree_impl_t *d, inode_t inode, bool do_link);
idaman dterr_t ida_export dirtree_rename(dirtree_impl_t *d, const char *from, const char *to);
idaman ssize_t ida_export dirtree_get_rank(const dirtree_impl_t *d, diridx_t diridx, const direntry_t &de);
idaman dterr_t ida_export dirtree_change_rank(dirtree_impl_t *d, const char *path, ssize_t rank_delta);
idaman void ida_export dirtree_get_parent_cursor(dirtree_cursor_t *out, const dirtree_impl_t *d, const dirtree_cursor_t &cursor);
idaman void ida_export notify_dirtree(dirtree_impl_t *d, bool added, inode_t inode);
idaman const char *ida_export dirtree_get_nodename(const dirtree_impl_t *d);
idaman void ida_export dirtree_set_nodename(dirtree_impl_t *d, const char *nm);
#endif // SWIG
//------------------------------------------------------------------------
/// Directory tree.
/// This class organizes a virtual directory tree over items that
/// are represented by dirspec_t.
class dirtree_t
{
dirtree_impl_t *d;
public:
//lint -sem(dirtree_t::dirtree_t, custodial(1))
dirtree_t(dirspec_t *ds) { d = create_dirtree(this, ds); }
~dirtree_t() { delete_dirtree(d); }
/// Get textual representation of the error code
static const char *errstr(dterr_t err) { return dirtree_errstr(err); }
/// Change current directory
/// \param path new current directory
/// \return \ref dterr_t error code
dterr_t chdir(const char *path) { return dirtree_chdir(d, path); }
/// Get current directory
/// \return the current working directory
qstring getcwd() const
{
qstring out;
dirtree_getcwd(&out, d);
return out;
}
/// Get absolute path pointed by the cursor
/// \param cursor
/// \return path; empty string if error
/// \note see also resolve_cursor()
qstring get_abspath(const dirtree_cursor_t &cursor) const
{
qstring out;
dirtree_get_abspath_by_cursor(&out, d, cursor);
return out;
}
/// Construct an absolute path from the specified relative path.
/// This function verifies the directory part of the specified path.
/// The last component of the specified path is not verified.
/// \param relpath relative path
/// \return path. empty path means wrong directory part of RELPATH
qstring get_abspath(const char *relpath) const
{
qstring out;
dirtree_get_abspath_by_relpath(&out, d, relpath);
return out;
}
/// Resolve cursor
/// \param cursor to analyze
/// \return directory entry;
/// if the cursor is bad, the resolved entry will be invalid.
/// \note see also get_abspath()
direntry_t resolve_cursor(const dirtree_cursor_t &cursor) const
{
direntry_t de;
dirtree_resolve_cursor(&de, d, cursor);
return de;
}
/// Resolve path
/// \param path to analyze
/// \return directory entry
direntry_t resolve_path(const char *path) const
{
direntry_t de;
dirtree_resolve_path(&de, d, path);
return de;
}
static bool isdir(const direntry_t &de) { return de.valid() && de.isdir; }
static bool isfile(const direntry_t &de) { return de.valid() && !de.isdir; }
/// Is a directory?
/// \param path to analyze
/// \return true if the specified path is a directory
bool isdir(const char *path) const
{
direntry_t de = resolve_path(path);
return isdir(de);
}
/// Is a file?
/// \param path to analyze
/// \return true if the specified path is a file
bool isfile(const char *path) const
{
direntry_t de = resolve_path(path);
return isfile(de);
}
/// Get entry name
/// \param de directory entry
/// \return name
qstring get_entry_name(const direntry_t &de) const
{
qstring out;
dirtree_get_entry_name(&out, d, de);
return out;
}
/// Get dir size
/// \param diridx directory index
/// \return number of entries under this directory;
/// if error, return -1
ssize_t get_dir_size(diridx_t diridx) const { return dirtree_get_dir_size(d, diridx); }
/// Get entry attributes
/// \param de directory entry
/// \return name
qstring get_entry_attrs(const direntry_t &de) const
{
qstring out;
dirtree_get_entry_attrs(&out, d, de);
return out;
}
/// Start iterating over files in a directory
/// \param ff directory iterator. it will be initialized by the function
/// \param pattern pattern to search for
/// \return success
bool findfirst(dirtree_iterator_t *ff, const char *pattern) const
{
return dirtree_findfirst(d, ff, pattern);
}
/// Continue iterating over files in a directory
/// \param ff directory iterator
/// \return success
bool findnext(dirtree_iterator_t *ff) const
{
return dirtree_findnext(d, ff);
}
/// Create a directory.
/// \param path directory to create
/// \return \ref dterr_t error code
dterr_t mkdir(const char *path) { return dirtree_mkdir(d, path); }
/// Remove a directory.
/// \param path directory to delete
/// \return \ref dterr_t error code
dterr_t rmdir(const char *path) { return dirtree_rmdir(d, path); }
/// Add a file item into a directory.
/// \param path path to item to add to a directory
/// \return \ref dterr_t error code
dterr_t link(const char *path) { return dirtree_link(d, path, true); }
/// Remove a file item from a directory.
/// \param path path to item remove from a directory
/// \return \ref dterr_t error code
dterr_t unlink(const char *path) { return dirtree_link(d, path, false); }
/// Add an inode into the current directory
/// \param inode
/// \return \ref dterr_t error code
dterr_t link(inode_t inode) { return dirtree_link_inode(d, inode, true); }
/// Remove an inode from the current directory
/// \param inode
/// \return \ref dterr_t error code
dterr_t unlink(inode_t inode) { return dirtree_link_inode(d, inode, false); }
/// Rename a directory entry.
/// \param from source path
/// \param to destination path
/// \return \ref dterr_t error code
/// \note This function can also rename the item
dterr_t rename(const char *from, const char *to)
{
return dirtree_rename(d, from, to);
}
/// Get ordering rank of an item.
/// \param diridx index of the parent directory
/// \param de directory entry
/// \return number in a range of [0..n) where n is the number of entries in
/// the parent directory. -1 if error
ssize_t get_rank(diridx_t diridx, const direntry_t &de) const
{
return dirtree_get_rank(d, diridx, de);
}
/// Change ordering rank of an item.
/// \param path path to the item
/// \param delta the amount of the change. positive numbers mean to move down
/// in the list; negative numbers mean to move up.
/// \return \ref dterr_t error code
/// \note All subdirectories go before all file entries.
dterr_t change_rank(const char *path, ssize_t rank_delta)
{
return dirtree_change_rank(d, path, rank_delta);
}
/// Get parent cursor.
/// \param cursor a valid ditree cursor
/// \return cursor's parent
dirtree_cursor_t get_parent_cursor(const dirtree_cursor_t &cursor) const
{
dirtree_cursor_t parent;
dirtree_get_parent_cursor(&parent, d, cursor);
return parent;
}
/// Load the tree structure from the netnode.
/// If dirspec_t::nodename is empty, the operation will be considered a success.
/// In addition, calling load() more than once will not do anything,
/// and will be considered a success.
/// \return success
/// \see dirspec_t::nodename.
bool load()
{
return load_dirtree(d);
}
/// Save the tree structure to the netnode.
/// \return success
/// \see dirspec_t::nodename.
bool save() const
{
return save_dirtree(d);
}
/// netnode name
const char *get_nodename() const
{
return dirtree_get_nodename(d);
}
void set_nodename(const char *nm)
{
return dirtree_set_nodename(d, nm);
}
/// Notify dirtree about a change of an inode.
/// \param add are we adding or deleting an inode?
/// \param inode inode in question
void notify_dirtree(bool added, inode_t inode)
{
::notify_dirtree(d, added, inode);
}
};
/// Built-in dirtree specializations:
enum dirtree_id_t
{
DIRTREE_LOCAL_TYPES,
DIRTREE_STRUCTS,
DIRTREE_ENUMS,
DIRTREE_FUNCS,
DIRTREE_NAMES,
DIRTREE_IMPORTS,
DIRTREE_IDAPLACE_BOOKMARKS,
DIRTREE_STRUCTS_BOOKMARKS,
DIRTREE_ENUMS_BOOKMARKS,
DIRTREE_END,
};
idaman dirtree_t *ida_export get_std_dirtree(dirtree_id_t id);
#endif // define DIRTREE_HPP

621
idasdk76/include/diskio.hpp Normal file
View File

@@ -0,0 +1,621 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef _DISKIO_HPP
#define _DISKIO_HPP
#include <stdio.h>
/*! \file diskio.hpp
\brief File I/O functions for IDA
You should not use standard C file I/O functions in modules.
Use functions from this header, pro.h and fpro.h instead.
This file also declares a call_system() function.
*/
//-------------------------------------------------------------------------
// S E A R C H F O R F I L E S
//-------------------------------------------------------------------------
/// Get IDA directory (if subdir==NULL)
/// or the specified subdirectory (see \ref SUBDIR)
idaman THREAD_SAFE const char *ida_export idadir(const char *subdir);
/// Search for IDA system file.
/// This function searches for a file in:
/// -# each directory specified by %IDAUSR%
/// -# ida directory [+ subdir]
/// and returns the first match.
/// \param[out] buf buffer for file name
/// \param bufsize size of output buffer
/// \param filename name of file to search
/// \param subdir if specified, the file is looked for in the specified subdirectory
/// of the ida directory first (see \ref SUBDIR)
/// \return NULL if not found, otherwise a pointer to full file name.
idaman THREAD_SAFE char *ida_export getsysfile(
char *buf,
size_t bufsize,
const char *filename,
const char *subdir);
/// \defgroup SUBDIR IDA subdirectories
/// Passed as 'subdir' parameter to idadir(), getsysfile(), and others.
//@{
#define CFG_SUBDIR "cfg"
#define IDC_SUBDIR "idc"
#define IDS_SUBDIR "ids"
#define IDP_SUBDIR "procs"
#define LDR_SUBDIR "loaders"
#define SIG_SUBDIR "sig"
#define TIL_SUBDIR "til"
#define PLG_SUBDIR "plugins"
#define THM_SUBDIR "themes"
//@}
/// Get user ida related directory.
/// \code
/// - if $IDAUSR is defined:
/// - the first element in $IDAUSR
/// - else
/// - default user directory ($HOME/.idapro or %APPDATA%Hex-Rays/IDA Pro)
/// \endcode
idaman THREAD_SAFE const char *ida_export get_user_idadir(void);
/// Get list of directories in which to find a specific IDA resource
/// (see \ref SUBDIR). The order of the resulting list is as follows:
/// \code
/// - [$IDAUSR/subdir (0..N entries)]
/// - $IDADIR/subdir
/// \endcode
/// \param[out] dirs output vector for directory names
/// \param subdir name of the resource to list
/// \param flags \ref IDA_SUBDIR_ bits
/// \return number of directories appended to 'dirs'
idaman THREAD_SAFE int ida_export get_ida_subdirs(qstrvec_t *dirs, const char *subdir, int flags=0);
/// \defgroup IDA_SUBDIR_ Subdirectory modification flags
/// Passed as 'flags' parameter to get_ida_subdirs()
//@{
#define IDA_SUBDIR_IDP 0x0001 ///< append the processor name as a subdirectory
#define IDA_SUBDIR_IDADIR_FIRST 0x0002 ///< $IDADIR/subdir will be first, not last
#define IDA_SUBDIR_ONLY_EXISTING 0x0004 ///< only existing directories will be present
//@}
/// Get a folder location by CSIDL (see \ref CSIDL).
/// Path should be of at least MAX_PATH size
idaman THREAD_SAFE bool ida_export get_special_folder(char *buf, size_t bufsize, int csidl);
/// \defgroup CSIDL Common CSIDLs
/// Passed as 'csidl' parameter to get_special_folder()
//@{
#ifndef CSIDL_APPDATA
#define CSIDL_APPDATA 0x001a
#endif
#ifndef CSIDL_LOCAL_APPDATA
#define CSIDL_LOCAL_APPDATA 0x001c
#endif
#ifndef CSIDL_PROGRAM_FILES
#define CSIDL_PROGRAM_FILES 0x0026
#endif
#ifndef CSIDL_PROGRAM_FILES_COMMON
#define CSIDL_PROGRAM_FILES_COMMON 0x002b
#endif
#ifndef CSIDL_PROGRAM_FILESX86
#define CSIDL_PROGRAM_FILESX86 0x002a
#endif
//@}
/// Enumerate files in the specified directory.
/// \param[out] answer buffer to contain the file name for which
/// file_enumerator_t::visit_file returns non-zero value
/// (may be NULL)
/// \param answer_size size of 'answer'
/// \param path directory to enumerate files in
/// \param fname mask of file names to enumerate
/// \param fv file_enumerator_t::visit_file function called for each file
/// - file: full file name (with path)
/// - if returns non-zero value, the enumeration
/// is stopped and the return code is
/// is returned to the caller.
/// the callback function
/// \return zero or the code returned by 'func'
struct file_enumerator_t
{
virtual int visit_file(const char *file) = 0;
virtual ~file_enumerator_t() {}
};
idaman THREAD_SAFE int ida_export enumerate_files2(
char *answer,
size_t answer_size,
const char *path,
const char *fname,
file_enumerator_t &fv);
//-------------------------------------------------------------------------
// O P E N / R E A D / W R I T E / C L O S E F I L E S
//-------------------------------------------------------------------------
/// \name Open/Read/Write/Close Files
/// There are two sets of "open file" functions.
/// The first set tries to open a file and returns success or failure.
/// The second set is "open or die": if the file cannot be opened
/// then the function will display an error message and exit.
//@{
/// Open a new file for write in text mode, deny write.
/// If a file exists, it will be removed.
/// \return NULL if failure
idaman THREAD_SAFE FILE *ida_export fopenWT(const char *file);
/// Open a new file for write in binary mode, deny read/write.
/// If a file exists, it will be removed.
/// \return NULL if failure
idaman THREAD_SAFE FILE *ida_export fopenWB(const char *file);
/// Open a file for read in text mode, deny none.
/// \return NULL if failure
idaman THREAD_SAFE FILE *ida_export fopenRT(const char *file);
/// Open a file for read in binary mode, deny none.
/// \return NULL if failure
idaman THREAD_SAFE FILE *ida_export fopenRB(const char *file);
/// Open a file for read/write in binary mode, deny write.
/// \return NULL if failure
idaman THREAD_SAFE FILE *ida_export fopenM(const char *file);
/// Open a file for append in text mode, deny none.
/// \return NULL if failure
idaman THREAD_SAFE FILE *ida_export fopenA(const char *file);
/// Open a file for read in binary mode or die, deny none.
/// If a file cannot be opened, this function displays a message and exits.
idaman THREAD_SAFE FILE *ida_export openR(const char *file);
/// Open a file for read in text mode or die, deny none.
/// If a file cannot be opened, this function displays a message and exits.
idaman THREAD_SAFE FILE *ida_export openRT(const char *file);
/// Open a file for read/write in binary mode or die, deny write.
/// If a file cannot be opened, this function displays a message and exits.
idaman THREAD_SAFE FILE *ida_export openM(const char *file);
//@}
//-------------------------------------------------------------------------
// F I L E S I Z E / D I S K S P A C E
//-------------------------------------------------------------------------
/// Get length of file in bytes.
/// \param fp pointer to file
idaman THREAD_SAFE uint64 ida_export qfsize(FILE *fp);
/// Change size of file or die.
/// If an error occurs, this function displays a message and exits.
/// \param fp pointer to file
/// \param size new size of file
idaman THREAD_SAFE void ida_export echsize(FILE *fp, uint64 size);
/// Get free disk space in bytes.
/// \param path name of any directory on the disk to get information about
idaman THREAD_SAFE uint64 ida_export get_free_disk_space(const char *path);
//-------------------------------------------------------------------------
// I / O P O R T D E F I N I T I O N S F I L E
//-------------------------------------------------------------------------
/// Describes an I/O port bit
struct ioport_bit_t
{
qstring name; ///< name of the bit
qstring cmt; ///< comment
};
DECLARE_TYPE_AS_MOVABLE(ioport_bit_t);
typedef qvector<ioport_bit_t> ioport_bits_t;
/// Describes an I/O port
struct ioport_t
{
ea_t address; ///< address of the port
qstring name; ///< name of the port
qstring cmt; ///< comment
ioport_bits_t bits; ///< bit names
void *userdata; ///< arbitrary data. initialized to NULL.
ioport_t()
: address(0), userdata(NULL)
{
}
};
DECLARE_TYPE_AS_MOVABLE(ioport_t);
typedef qvector<ioport_t> ioports_t;
/// Read i/o port definitions from a config file.
///
/// Each device definition in the input file begins with a line like this:
///
/// \v{.devicename}
///
/// After it go the port definitions in this format:
///
/// \v{portname address}
///
/// The bit definitions (optional) are represented like this:
///
/// \v{portname.bitname bitnumber}
///
/// Lines beginning with a space are ignored.
/// comment lines should be started with ';' character.
///
/// The default device is specified at the start of the file:
///
/// \v{.default device_name}
///
/// \note It is permissible to have a symbol mapped to several addresses
/// but all addresses must be unique.
/// \param[out] ports output vector
/// \param device contains device name to load. If default_device[0] == 0
/// then the default device is determined by .default directive
/// in the config file.
/// \param file config file name
/// \param callback callback to call when the input line can't be parsed normally.
/// - line: input line to parse
/// - returns error message. if NULL, then the line is parsed ok.
/// \return -1 on error or size of vector
idaman THREAD_SAFE ssize_t ida_export read_ioports(
ioports_t *ports,
qstring *device,
const char *file,
const char *(idaapi *callback)(
const ioports_t &ports,
const char *line)=NULL);
struct ioports_fallback_t
{
// returns success or fills ERRBUF with an error message
virtual bool handle(qstring *errbuf, const ioports_t &ports, const char *line) = 0;
};
idaman THREAD_SAFE ssize_t ida_export read_ioports2(
ioports_t *ports,
qstring *device,
const char *file,
ioports_fallback_t *callback=nullptr);
/// Allow the user to choose the ioport device.
/// \param[in,out] device in: contains default device name. If default_device[0] == 0
/// then the default device is determined by .default directive
/// in the config file.
/// out: the selected device name
/// \param file config file name
/// \param parse_params if present (non NULL), then defines a callback which
/// will be called for all lines not starting with a dot (.)
/// This callback may parse these lines are prepare a simple
/// processor parameter string. This string will be displayed
/// along with the device name.
/// If it returns #IOPORT_SKIP_DEVICE, then the current
/// device will not be included in the list.
/// \retval true the user selected a device, its name is in 'device'
/// \retval false the selection was cancelled. if device=="NONE" upon return,
/// then no devices were found in the configuration file
idaman THREAD_SAFE bool ida_export choose_ioport_device(
qstring *_device,
const char *file,
const char *(idaapi *parse_params)(
qstring *buf,
const char *line)=NULL);
struct choose_ioport_parser_t
{
/// \retval true and fill PARAM with a displayed string
/// \retval false and empty PARAM to skip the current device
/// \retval false and fill PARAM with an error message
virtual bool parse(qstring *param, const char *line) = 0;
};
idaman THREAD_SAFE bool ida_export choose_ioport_device2(
qstring *_device,
const char *file,
choose_ioport_parser_t *parse_params);
/// See 'parse_params' parameter to choose_ioport_device()
#define IOPORT_SKIP_DEVICE ((const char *)(-1))
/// Find ioport in the array of ioports
idaman THREAD_SAFE const ioport_t *ida_export find_ioport(const ioports_t &ports, ea_t address);
/// Find ioport bit in the array of ioports
idaman THREAD_SAFE const ioport_bit_t *ida_export find_ioport_bit(const ioports_t &ports, ea_t address, size_t bit);
//-------------------------------------------------------------------------
// S Y S T E M S P E C I F I C C A L L S
//-------------------------------------------------------------------------
/// Execute a operating system command.
/// This function suspends the interface (Tvision), runs the command
/// and redraws the screen.
/// \param command command to execute. If NULL, an interactive shell is activated
/// \return the error code returned by system() call
idaman THREAD_SAFE int ida_export call_system(const char *command);
//-------------------------------------------------------------------------
// L O A D E R I N P U T S O U R C E F U N C T I O N S
//-------------------------------------------------------------------------
/// \name Loader Input Source
/// Starting with v4.8 IDA can load and run remote files.
/// In order to do that, we replace the FILE* in the loader modules
/// with an abstract input source (linput_t). The source might be linked to
/// a local or remote file.
//@{
class linput_t; ///< loader input source
/// linput types
enum linput_type_t
{
LINPUT_NONE, ///< invalid linput
LINPUT_LOCAL, ///< local file
LINPUT_RFILE, ///< remote file (\dbg{open_file}, \dbg{read_file})
LINPUT_PROCMEM, ///< debugged process memory (read_dbg_memory())
LINPUT_GENERIC ///< generic linput
};
/// Read the input source.
/// If failed, inform the user and ask him if he wants to continue.
/// If he does not, this function will not return (loader_failure() will be called).
/// This function may be called only from loaders!
idaman void ida_export lread(linput_t *li, void *buf, size_t size);
/// Read the input source.
/// \return number of read bytes or -1
idaman ssize_t ida_export qlread(linput_t *li, void *buf, size_t size);
/// Read one line from the input source.
/// \return NULL if failure, otherwise 's'
idaman char *ida_export qlgets(char *s, size_t len, linput_t *li);
/// Read one character from the input source.
/// \return EOF if failure, otherwise the read character
idaman int ida_export qlgetc(linput_t *li);
/// Read multiple bytes and swap if necessary.
/// \param li input file
/// \param buf pointer to output buffer
/// \param size number of bytes to read
/// \param mf big endian?
/// \retval 0 ok
/// \retval -1 failure
idaman int ida_export lreadbytes(linput_t *li, void *buf, size_t size, bool mf);
/// Helper to define lread2bytes(), lread4bytes(), etc
#define DEF_LREADBYTES(read, type, size) \
/*! \brief Read a value from linput - also see lreadbytes() */ \
inline int idaapi read(linput_t *li, type *res, bool mf) \
{ return lreadbytes(li, res, size, mf); }
DEF_LREADBYTES(lread2bytes, int16, 2)
DEF_LREADBYTES(lread2bytes, uint16, 2)
DEF_LREADBYTES(lread4bytes, int32, 4)
DEF_LREADBYTES(lread4bytes, uint32, 4)
DEF_LREADBYTES(lread8bytes, int64, 8)
DEF_LREADBYTES(lread8bytes, uint64, 8)
#undef DEF_LREADBYTES
/// Read a zero-terminated string from the input.
/// If fpos == -1 then no seek will be performed.
idaman char *ida_export qlgetz(
linput_t *li,
int64 fpos,
char *buf,
size_t bufsize);
/// Get the input source size
idaman int64 ida_export qlsize(linput_t *li);
/// Set input source position.
/// \return the new position (not 0 as fseek!)
idaman qoff64_t ida_export qlseek(linput_t *li, qoff64_t pos, int whence=SEEK_SET);
/// Get input source position
inline qoff64_t idaapi qltell(linput_t *li) { return qlseek(li, 0, SEEK_CUR); }
/// Open loader input
idaman linput_t *ida_export open_linput(const char *file, bool remote);
/// Close loader input
idaman THREAD_SAFE void ida_export close_linput(linput_t *li);
/// Get FILE* from the input source.
/// If the input source is linked to a remote file, then return NULL.
/// Otherwise return the underlying FILE*
/// Please do not use this function if possible.
idaman THREAD_SAFE FILE *ida_export qlfile(linput_t *li);
/// Convert FILE * to input source.
/// Used for temporary linput_t objects - call unmake_linput() to free
/// the slot after the use.
idaman THREAD_SAFE linput_t *ida_export make_linput(FILE *fp);
/// Free an linput_t object (also see make_linput())
idaman THREAD_SAFE void ida_export unmake_linput(linput_t *li);
/// Generic linput class - may be used to create a linput_t instance for
/// any data source
struct generic_linput_t
{
/// \name Warning
/// The following two fields must be filled before calling create_generic_linput()
//@{
uint64 filesize; ///< input file size
uint32 blocksize; ///< preferred block size to work with
///< read/write sizes will be in multiples of this number.
///< for example, 4096 is a nice value
///< blocksize 0 means that the filesize is unknown.
///< the internal cache will be disabled in this case.
///< also, seeks from the file end will fail.
///< blocksize=-1 means error.
//@}
virtual ssize_t idaapi read(qoff64_t off, void *buffer, size_t nbytes) = 0;
virtual ~generic_linput_t() {}
};
/// Create a generic linput
/// \param gl linput description.
/// this object will be destroyed by close_linput()
/// using "delete gl;"
idaman THREAD_SAFE linput_t *ida_export create_generic_linput(generic_linput_t *gl);
/// Trivial memory linput
idaman THREAD_SAFE linput_t *ida_export create_bytearray_linput(const uchar *start, size_t size);
/// Create a linput for process memory.
/// This linput will use read_dbg_memory() to read data.
/// \param start starting address of the input
/// \param size size of the memory area to represent as linput
/// if unknown, may be passed as 0
idaman linput_t *ida_export create_memory_linput(ea_t start, asize_t size);
/// Get linput type
inline THREAD_SAFE linput_type_t idaapi get_linput_type(linput_t *li)
{
return *(linput_type_t *)li;
}
/// Object that will free an linput_t at destruction-time
typedef janitor_t<linput_t*> linput_janitor_t;
/// Free the linput_t
template <> inline linput_janitor_t::~janitor_t()
{
close_linput(resource);
}
//---------------------------------------------------------------------------
/// Helper class - adapts linput to be used in extract_... functions
/// as a data supplier (see kernwin.hpp)
class linput_buffer_t
{
public:
linput_buffer_t(linput_t *linput, int64 size=0): li(linput), lsize(size) {}
ssize_t read(void *buf, size_t n)
{
return qlread(li, buf, n);
}
bool eof()
{
if ( lsize == 0 )
lsize = qlsize(li);
return qltell(li) >= lsize;
}
protected:
linput_t *li;
private:
int64 lsize;
};
//@}
// -------------------------------------------------------------------------
#ifndef NO_OBSOLETE_FUNCS
idaman DEPRECATED THREAD_SAFE FILE *ida_export ecreate(const char *file);
idaman DEPRECATED THREAD_SAFE void ida_export eclose(FILE *fp);
idaman DEPRECATED THREAD_SAFE void ida_export eread(FILE *fp, void *buf, size_t size);
idaman DEPRECATED THREAD_SAFE void ida_export ewrite(FILE *fp, const void *buf, size_t size);
idaman DEPRECATED THREAD_SAFE void ida_export eseek(FILE *fp, qoff64_t pos);
idaman DEPRECATED THREAD_SAFE int ida_export enumerate_files(
char *answer,
size_t answer_size,
const char *path,
const char *fname,
int (idaapi*func)(const char *file,void *ud),
void *ud=NULL);
#endif
#endif // _DISKIO_HPP

106
idasdk76/include/entry.hpp Normal file
View File

@@ -0,0 +1,106 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef _ENTRY_HPP
#define _ENTRY_HPP
/*! \file entry.hpp
\brief Functions that deal with entry points
Exported functions are considered as entry points as well.
IDA maintains list of entry points to the program.
Each entry point:
- has an address
- has a name
- may have an ordinal number
*/
/// Get number of entry points
idaman size_t ida_export get_entry_qty(void);
/// \defgroup AEF_ entry flags
/// Passed as 'flags' parameter to add_entry(ea_t, const char *, int)
//@{
#define AEF_UTF8 0x0 ///< the name is given in UTF-8 (default)
#define AEF_IDBENC 0x1 ///< the name is given in the IDB encoding;
///< non-ASCII bytes will be decoded accordingly
///< Specifying AEF_IDBENC also implies AEF_NODUMMY
#define AEF_NODUMMY 0x2 ///< automatically prepend the name with '_' if
///< it begins with a dummy suffix. See also AEF_IDBENC
//@}
/// Add an entry point to the list of entry points.
/// \param ord ordinal number
/// if ordinal number is equal to 'ea' then ordinal is not used
/// \param ea linear address
/// \param name name of entry point. If the specified location already
/// has a name, the old name will be appended to the regular
/// comment. If name == NULL, then the old name will be retained.
/// \param makecode should the kernel convert bytes at the entry point
/// to instruction(s)
/// \param flags See AEF_*
/// \return success (currently always true)
idaman bool ida_export add_entry(uval_t ord, ea_t ea, const char *name, bool makecode, int flags=AEF_UTF8);
/// Get ordinal number of an entry point.
/// \param idx internal number of entry point. Should be
/// in the range 0..get_entry_qty()-1
/// \return ordinal number or 0.
idaman uval_t ida_export get_entry_ordinal(size_t idx);
/// Get entry point address by its ordinal
/// \param ord ordinal number of entry point
/// \return address or #BADADDR
idaman ea_t ida_export get_entry(uval_t ord);
/// Get name of the entry point by its ordinal.
/// \param buf output buffer, may be NULL
/// \param ord ordinal number of entry point
/// \return size of entry name or -1
idaman ssize_t ida_export get_entry_name(qstring *buf, uval_t ord);
/// Rename entry point.
/// \param ord ordinal number of the entry point
/// \param name name of entry point. If the specified location already
/// has a name, the old name will be appended to a repeatable
/// comment.
/// \param flags See AEF_*
/// \return success
idaman bool ida_export rename_entry(uval_t ord, const char *name, int flags=AEF_UTF8);
/// Set forwarder name for ordinal.
/// \param ord ordinal number of the entry point
/// \param name forwarder name for entry point.
/// \param flags See AEF_*
/// \return success
idaman bool ida_export set_entry_forwarder(uval_t ord, const char *name, int flags=AEF_UTF8);
/// Get forwarder name for the entry point by its ordinal.
/// \param buf output buffer, may be NULL
/// \param ord ordinal number of entry point
/// \return size of entry forwarder name or -1
idaman ssize_t ida_export get_entry_forwarder(qstring *buf, uval_t ord);
#endif // _ENTRY_HPP

371
idasdk76/include/enum.hpp Normal file
View File

@@ -0,0 +1,371 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
* Enums and bitfields
* Bitfields will be abbreviated as "bf".
*
*/
#ifndef _ENUM_HPP
#define _ENUM_HPP
#include <nalt.hpp>
/*! \file enum.hpp
\brief Assembly level enum management
Enums and bitfields are represented as ::enum_t.
*/
typedef tid_t enum_t; ///< Enums and bitfields
typedef uval_t bmask_t; ///< unsigned value that describes a bitmask
///< a bit mask is 32/64 bits.
#define DEFMASK (bmask_t(-1)) ///< default bitmask
typedef uval_t const_t; ///< members of enums
/// Max number of identical constants allowed for one enum type
const uchar MAX_ENUM_SERIAL = 255;
/// Get number of declared ::enum_t types
idaman size_t ida_export get_enum_qty(void);
/// Get enum by its index in the list of enums (0..get_enum_qty()-1).
idaman enum_t ida_export getn_enum(size_t idx);
/// Get the index in the list of enums
idaman uval_t ida_export get_enum_idx(enum_t id);
/// Get enum by name
idaman enum_t ida_export get_enum(const char *name);
/// Is enum a bitfield?
/// (otherwise - plain enum, no bitmasks except for #DEFMASK are allowed)
idaman bool ida_export is_bf(enum_t id);
/// Is enum collapsed?
idaman bool ida_export is_enum_hidden(enum_t id);
/// Collapse enum
idaman bool ida_export set_enum_hidden(enum_t id, bool hidden);
/// Does enum come from type library?
idaman bool ida_export is_enum_fromtil(enum_t id);
/// Specify that enum comes from a type library
idaman bool ida_export set_enum_fromtil(enum_t id, bool fromtil);
/// Is a ghost copy of a local type?
idaman bool ida_export is_ghost_enum(enum_t id);
/// Specify that enum is a ghost copy of a local type
idaman bool ida_export set_enum_ghost(enum_t id, bool ghost);
/// Get name of enum
idaman ssize_t ida_export get_enum_name(qstring *out, enum_t id);
/// Get name of enum
/// \param[out] out buffer to hold the name
/// \param id enum id
/// \param flags \ref ENFL_
idaman ssize_t ida_export get_enum_name2(qstring *out, enum_t id, int flags=0);
/// \defgroup ENFL_ Enum name flags
/// Passed as 'flags' parameter to get_enum_name()
//@{
#define ENFL_REGEX 0x0001 ///< apply regular expressions to beautify the name
//@}
inline qstring get_enum_name(tid_t id, int flags=0)
{
qstring name;
get_enum_name2(&name, id, flags);
return name;
}
/// Get the width of a enum element
/// allowed values: 0 (unspecified),1,2,4,8,16,32,64
idaman size_t ida_export get_enum_width(enum_t id);
/// See comment for get_enum_width()
idaman bool ida_export set_enum_width(enum_t id, int width);
/// Get enum comment
idaman ssize_t ida_export get_enum_cmt(qstring *buf, enum_t id, bool repeatable);
/// Get the number of the members of the enum
idaman size_t ida_export get_enum_size(enum_t id);
/// Get flags determining the representation of the enum.
/// (currently they define the numeric base: octal, decimal, hex, bin) and signness.
idaman flags_t ida_export get_enum_flag(enum_t id);
/// Get a reference to an enum member by its name
idaman const_t ida_export get_enum_member_by_name(const char *name);
/// Get value of an enum member
idaman uval_t ida_export get_enum_member_value(const_t id);
/// Get the parent enum of an enum member
idaman enum_t ida_export get_enum_member_enum(const_t id);
/// Get bitmask of an enum member
idaman bmask_t ida_export get_enum_member_bmask(const_t id);
/// Find an enum member by enum, value and bitmask
/// \note if serial -1, return a member with any serial
idaman const_t ida_export get_enum_member(enum_t id, uval_t value, int serial, bmask_t mask);
/// \name Access to all used bitmasks in an enum
//@{
/// Get first bitmask in the enum (bitfield)
///
/// \param enum_id id of enum (bitfield)
/// \return the smallest bitmask for enum, or DEFMASK
///
idaman bmask_t ida_export get_first_bmask(enum_t id);
/// Get last bitmask in the enum (bitfield)
///
/// \param enum_id id of enum
/// \return the biggest bitmask for enum, or DEFMASK
idaman bmask_t ida_export get_last_bmask(enum_t id);
/// Get next bitmask in the enum (bitfield)
///
/// \param enum_id id of enum
/// \param value the current bitmask
/// \return value of a bitmask with value higher than the specified value, or DEFMASK
idaman bmask_t ida_export get_next_bmask(enum_t id, bmask_t bmask);
/// Get prev bitmask in the enum (bitfield)
///
/// \param enum_id id of enum
/// \param value the current bitmask
/// \return value of a bitmask with value lower than the specified value, or DEFMASK
idaman bmask_t ida_export get_prev_bmask(enum_t id, bmask_t bmask);
//@}
/// \name Access to all enum members with specified bitmask
/// \note these functions return values, not ::const_t!
//@{
idaman uval_t ida_export get_first_enum_member(enum_t id, bmask_t bmask=DEFMASK);
idaman uval_t ida_export get_last_enum_member(enum_t id, bmask_t bmask=DEFMASK);
idaman uval_t ida_export get_next_enum_member(enum_t id, uval_t value, bmask_t bmask=DEFMASK);
idaman uval_t ida_export get_prev_enum_member(enum_t id, uval_t value, bmask_t bmask=DEFMASK);
//@}
/// Get name of an enum member by const_t
idaman ssize_t ida_export get_enum_member_name(qstring *out, const_t id);
/// Get enum member's comment
idaman ssize_t ida_export get_enum_member_cmt(qstring *buf, const_t id, bool repeatable);
/// \name Access to all enum members with specified value and mask
/// A sample loop looks like this:
/// \code
/// const_t main_cid;
/// uchar serial;
/// for ( const_t cid=main_cid=get_first_serial_enum_member(&serial, id, v, mask);
/// cid != BADNODE;
/// cid = get_next_serial_enum_member(&serial, main_cid) )
/// {
/// ...
/// }
/// \endcode
/// The 'out_serial' argument of get_first_serial_enum_member/get_last_serial_enum_member can be NULL.
/// The 'in_out_serial' is required for the other functions.
//@{
idaman const_t ida_export get_first_serial_enum_member(uchar *out_serial, enum_t id, uval_t value, bmask_t bmask);
idaman const_t ida_export get_last_serial_enum_member(uchar *out_serial, enum_t id, uval_t value, bmask_t bmask);
idaman const_t ida_export get_next_serial_enum_member(uchar *in_out_serial, const_t first_cid);
idaman const_t ida_export get_prev_serial_enum_member(uchar *in_out_serial, const_t first_cid);
//@}
/// Enum member visitor - see for_all_enum_members().
/// Derive your visitor from this class.
struct enum_member_visitor_t
{
/// Implements action to take when enum member is visited.
/// \return nonzero to stop the iteration
virtual int idaapi visit_enum_member(const_t cid, uval_t value) = 0;
};
/// Visit all members of a given enum
idaman int ida_export for_all_enum_members(enum_t id, enum_member_visitor_t &cv);
/// Get serial number of an enum member
idaman uchar ida_export get_enum_member_serial(const_t cid);
/// Get corresponding type ordinal number
idaman int32 ida_export get_enum_type_ordinal(enum_t id);
/// Set corresponding type ordinal number
idaman void ida_export set_enum_type_ordinal(enum_t id, int32 ord);
//--------------------------------------------------------------------------
// MANIPULATION
/// Add new enum type.
/// - if idx==#BADADDR then add as the last idx
/// - if name==NULL then generate a unique name "enum_%d"
idaman enum_t ida_export add_enum(size_t idx, const char *name, flags_t flag);
/// Delete an enum type
idaman void ida_export del_enum(enum_t id);
/// Set serial number of enum.
/// Also see get_enum_idx().
idaman bool ida_export set_enum_idx(enum_t id, size_t idx);
/// Set 'bitfield' bit of enum (i.e. convert it to a bitfield)
idaman bool ida_export set_enum_bf(enum_t id, bool bf);
/// Set name of enum type
idaman bool ida_export set_enum_name(enum_t id,const char *name);
/// Set comment for enum type
idaman bool ida_export set_enum_cmt(enum_t id,const char *cmt,bool repeatable);
/// Set data representation flags
idaman bool ida_export set_enum_flag(enum_t id, flags_t flag);
/// Add member to enum type.
/// \return 0 if ok, otherwise one of \ref ENUM_MEMBER_
idaman int ida_export add_enum_member(
enum_t id,
const char *name,
uval_t value,
bmask_t bmask=DEFMASK);
/// \defgroup ENUM_MEMBER_ Add enum member result codes
/// Return values for add_enum_member()
//@{
#define ENUM_MEMBER_ERROR_NAME 1 ///< already have member with this name (bad name)
#define ENUM_MEMBER_ERROR_VALUE 2 ///< already have 256 members with this value
#define ENUM_MEMBER_ERROR_ENUM 3 ///< bad enum id
#define ENUM_MEMBER_ERROR_MASK 4 ///< bad bmask
#define ENUM_MEMBER_ERROR_ILLV 5 ///< bad bmask and value combination (~bmask & value != 0)
//@}
/// Delete member of enum type
idaman bool ida_export del_enum_member(enum_t id, uval_t value, uchar serial, bmask_t bmask);
/// Set name of enum member
idaman bool ida_export set_enum_member_name(const_t id, const char *name);
/// Set comment for enum member
inline bool set_enum_member_cmt(const_t id, const char *cmt, bool repeatable)
{
return set_enum_cmt(id, cmt, repeatable);
}
/// Is bitmask one bit?
inline THREAD_SAFE bool is_one_bit_mask(bmask_t mask)
{
return (mask & (mask-1)) == 0;
}
/// \name Work with the bitmask name & comment
//@{
idaman bool ida_export set_bmask_name(enum_t id, bmask_t bmask, const char *name);
idaman ssize_t ida_export get_bmask_name(qstring *out, enum_t id, bmask_t bmask);
idaman bool ida_export set_bmask_cmt(enum_t id, bmask_t bmask, const char *cmt, bool repeatable);
idaman ssize_t ida_export get_bmask_cmt(qstring *buf, enum_t id, bmask_t bmask, bool repeatable);
//@}
#endif // _ENUM_HPP

96
idasdk76/include/err.h Normal file
View File

@@ -0,0 +1,96 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef _ERR_H
#define _ERR_H
#include <errno.h>
/*! \file err.h
\brief Thread safe functions that deal with error codes
*/
/// Print error message to stderr (analog of perror)
idaman THREAD_SAFE AS_PRINTF(1, 0) void ida_export vqperror(const char *format, va_list va);
/// Get error description string.
/// if _qerrno=-1, get_qerrno() will be used
idaman THREAD_SAFE const char *ida_export qstrerror(error_t _qerrno);
/// A convenience function to generate error messages (returns "header: error message")
idaman THREAD_SAFE char *ida_export get_errdesc(const char *header, error_t _qerrno=-1);
/// Get error message for MS Windows error codes
idaman THREAD_SAFE char *ida_export winerr(int code);
/// errno or GetLastError() depending on the system.
idaman THREAD_SAFE int ida_export qerrcode(int new_code=-1);
/// Get error string corresponding to qerrcode().
/// if code == -1, then qerrcode() will be called.
idaman const char *ida_export qerrstr(int code=-1);
#ifdef __cplusplus
/// See vqperror()
THREAD_SAFE AS_PRINTF(1, 2) inline void qperror(const char *format, ...)
{
va_list va;
va_start(va, format);
vqperror(format, va);
va_end(va);
}
/// See set_qerrno()
THREAD_SAFE inline void set_errno(int code)
{
errno = code;
set_qerrno(eOS);
}
#endif
// Internal functions
/// \cond
// n=0..3
idaman THREAD_SAFE void ida_export set_error_data(int n, size_t data);
idaman THREAD_SAFE void ida_export set_error_string(int n, const char *str);
idaman THREAD_SAFE size_t ida_export get_error_data(int n);
idaman THREAD_SAFE const char *ida_export get_error_string(int n);
#define QPRM_TYPE(t,n,x) set_error_data(n-1, t(x))
#define QPRM_CHAR(n,x) QPRM_TYPE(char,n,x)
#define QPRM_SHORT(n,x) QPRM_TYPE(short,n,x)
#define QPRM_INT(n,x) QPRM_TYPE(int,n,x)
#define QPRM_INT32(n,x) QPRM_TYPE(int32,n,x)
#define QPRM_UCHAR(n,x) QPRM_TYPE(uchar,n,x)
#define QPRM_USHORT(n,x) QPRM_TYPE(ushort,n,x)
#define QPRM_UINT(n,x) QPRM_TYPE(uint,n,x)
#define QPRM_UINT32(n,x) QPRM_TYPE(uint32,n,x)
#define QPRM(n,x) set_error_string(n-1, x)
/// \endcond
#endif

67
idasdk76/include/exehdr.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
* EXE-file header layout
*
*/
#ifndef __EXEHDR_H
#define __EXEHDR_H
#pragma pack(push, 1)
struct exehdr
{
uint16 exe_ident;
#define EXE_ID 0x5A4D // 'MZ'
#define EXE_ID2 0x4D5A // 'ZM' (DOS works with this also)
uint16 PartPag;
uint16 PageCnt;
uint16 ReloCnt;
uint16 HdrSize;
uint16 MinMem;
uint16 MaxMem;
uint16 ReloSS;
uint16 ExeSP;
uint16 ChkSum;
uint16 ExeIP;
uint16 ReloCS;
uint16 TablOff;
uint16 Overlay;
/*
uint16 res[4]; // Reserved words
uint16 oemid; // OEM identifier (for e_oeminfo)
uint16 oeminfo; // OEM information; e_oemid specific
uint16 res2[10]; // Reserved words
uint32 lfanew; // File address of new exe header
*/
int32 CalcEXE_Length(void)
{
int32 len = PageCnt * 512L - HdrSize * 16;
if ( PartPag != 0 )
len -= 512 - PartPag;
return len;
}
void CalcEXE_Pages(int32 len)
{
PartPag = uint16(len % 512);
PageCnt = uint16(len / 512);
if ( PartPag != 0 )
PageCnt++;
}
};
struct exehdr_full: exehdr
{
uint16 res[4]; // Reserved words
uint16 oemid; // OEM identifier (for e_oeminfo)
uint16 oeminfo; // OEM information; e_oemid specific
uint16 res2[10]; // Reserved words
uint32 lfanew; // File address of new exe header
};
#define PSPsize 0x100
#define PE_PTROFF 0x3C
#pragma pack(pop)
#endif

1192
idasdk76/include/expr.hpp Normal file

File diff suppressed because it is too large Load Diff

572
idasdk76/include/fixup.hpp Normal file
View File

@@ -0,0 +1,572 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef FIXUP_HPP
#define FIXUP_HPP
#include <nalt.hpp>
#include <segment.hpp>
/*! \file fixup.hpp
\brief Functions that deal with fixup information
A loader should setup fixup information using set_fixup().
*/
//--------------------------------------------------------------------------
/// Fixup information structure
typedef uint16 fixup_type_t; ///< see \ref fixup_type_t
/// \defgroup fixup_type_t Types of fixups
/// Fixup may be of standard or custom type. Standard types fall into
/// range 1..FIXUP_CUSTOM-1 and custom types fall into range
/// FIXUP_CUSTOM..MAX_UINT16.
/// \note Fixup type 0 is unused.
/// \name Fixup standard types
//@{
#define FIXUP_OFF8 13 ///< 8-bit offset
#define FIXUP_OFF16 1 ///< 16-bit offset
#define FIXUP_SEG16 2 ///< 16-bit base--logical segment base (selector)
#define FIXUP_PTR16 3 ///< 32-bit long pointer (16-bit base:16-bit
///< offset)
#define FIXUP_OFF32 4 ///< 32-bit offset
#define FIXUP_PTR32 5 ///< 48-bit pointer (16-bit base:32-bit offset)
#define FIXUP_HI8 6 ///< high 8 bits of 16bit offset
#define FIXUP_HI16 7 ///< high 16 bits of 32bit offset
#define FIXUP_LOW8 8 ///< low 8 bits of 16bit offset
#define FIXUP_LOW16 9 ///< low 16 bits of 32bit offset
#define V695_FIXUP_VHIGH 10 ///< obsolete
#define V695_FIXUP_VLOW 11 ///< obsolete
#define FIXUP_OFF64 12 ///< 64-bit offset
#define FIXUP_OFF8S 14 ///< 8-bit signed offset
#define FIXUP_OFF16S 15 ///< 16-bit signed offset
#define FIXUP_OFF32S 16 ///< 32-bit signed offset
//#define FIXUP_ 0xE
#define FIXUP_CUSTOM 0x8000 ///< start of the custom types range
//@}
/// Is fixup processed by processor module?
inline THREAD_SAFE bool is_fixup_custom(fixup_type_t type)
{
return (type & FIXUP_CUSTOM) != 0;
}
/// \defgroup FIXUPF_ Fixup flags
/// Used by fixup_data_t
//@{
/// fixup is relative to the linear address `base'. Otherwise fixup is
/// relative to the start of the segment with `sel' selector.
#define FIXUPF_REL 0x0001
/// target is a location (otherwise - segment).
/// Use this bit if the target is a symbol rather than an offset from the
/// beginning of a segment.
#define FIXUPF_EXTDEF 0x0002
/// fixup is ignored by IDA
/// - disallows the kernel to convert operands
/// - this fixup is not used during output
#define FIXUPF_UNUSED 0x0004
/// fixup was not present in the input file
#define FIXUPF_CREATED 0x0008
/// additional flags. The bits from this mask are not stored in the database
/// and can be used by the loader at its discretion.
#define FIXUPF_LOADER_MASK 0xF0000000
//@}
struct fixup_handler_t;
struct fixup_data_t
{
protected:
fixup_type_t type; // fixup type
uint32 flags; // FIXUPF_... bits
uval_t base; // base for relative fixups
public:
sel_t sel; ///< selector of the target segment.
///< BADSEL means an absolute (zero based) target.
///< \sa FIXUPF_REL
ea_t off; ///< target offset
///< \note The target is calculated as
///< `get_base() + off`.
adiff_t displacement; ///< displacement (offset from the target)
public:
fixup_data_t()
: type(0),
flags(0),
base(0),
sel(BADSEL),
off(0),
displacement(0) {}
fixup_data_t(fixup_type_t type_, uint32 flags_ = 0)
: type(type_),
flags(flags_),
base(0),
sel(BADSEL),
off(0),
displacement(0) {}
/// Fixup type \ref fixup_type_t
fixup_type_t get_type(void) const { return type; }
void set_type(fixup_type_t type_) { type = type_; }
void set_type_and_flags(fixup_type_t type_, uint32 flags_ = 0)
{
type = type_;
flags = flags_;
}
bool is_custom(void) const; ///< \ref is_fixup_custom()
/// Fixup flags \ref FIXUPF_
uint32 get_flags() const { return flags; }
bool is_extdef(void) const { return (flags & FIXUPF_EXTDEF) != 0; }
void set_extdef(void) { flags |= FIXUPF_EXTDEF; }
void clr_extdef(void) { flags &= ~FIXUPF_EXTDEF; }
bool is_unused(void) const { return (flags & FIXUPF_UNUSED) != 0; }
void set_unused(void) { flags |= FIXUPF_UNUSED; }
void clr_unused(void) { flags &= ~FIXUPF_UNUSED; }
/// Is fixup relative?
bool has_base(void) const { return (flags & FIXUPF_REL) != 0; }
/// Is fixup artificial?
bool was_created(void) const { return (flags & FIXUPF_CREATED) != 0; }
/// Get base of fixup.
/// \note The target is calculated as `get_base() + off`.
/// \sa FIXUPF_REL
ea_t get_base() const
{
return has_base() ? base : sel != BADSEL ? sel2ea(sel) : 0;
}
/// Set base of fixup.
/// The target should be set before a call of this function.
void set_base(ea_t new_base)
{
ea_t target = get_base() + off;
flags |= FIXUPF_REL;
base = new_base;
off = target - base;
}
void set_sel(const segment_t *seg)
{
sel = seg == NULL ? BADSEL : seg->sel;
}
/// Set selector of fixup to the target.
/// The target should be set before a call of this function.
void set_target_sel()
{
ea_t target = get_base() + off;
set_sel(getseg(target));
flags &= ~FIXUPF_REL;
base = 0; // just in case
off = target - get_base();
}
void set(ea_t source) const; ///< \ref set_fixup()
bool get(ea_t source); ///< \ref get_fixup()
/// \ref get_fixup_handler()
const fixup_handler_t *get_handler() const;
/// \ref get_fixup_desc()
const char *get_desc(qstring *buf, ea_t source) const;
// TODO rewrite to the inline implementation which uses
// fixup_handler_t::size
int calc_size() const; ///< \ref calc_fixup_size()
uval_t get_value(ea_t ea) const; ///< \ref get_fixup_value()
bool patch_value(ea_t ea) const; ///< \ref patch_fixup_value()
};
/// Get fixup information
idaman bool ida_export get_fixup(fixup_data_t *fd, ea_t source);
/// Check that a fixup exists at the given address
inline bool exists_fixup(ea_t source)
{
return get_fixup(NULL, source);
}
/// Set fixup information. You should fill ::fixup_data_t and call this
/// function and the kernel will remember information in the database.
/// \param source the fixup source address, i.e. the address modified by
/// the fixup
/// \param fd fixup data
idaman void ida_export set_fixup(ea_t source, const fixup_data_t &fd);
/// Delete fixup information
idaman void ida_export del_fixup(ea_t source);
/// \name Enumerate addresses with fixup information:
//@{
/// Get the first address with fixup information
///
/// \return the first address with fixup information, or BADADDR
idaman ea_t ida_export get_first_fixup_ea(void);
/// Find next address with fixup information
///
/// \param ea current address
/// \return the next address with fixup information, or BADADDR
idaman ea_t ida_export get_next_fixup_ea(ea_t ea);
/// Find previous address with fixup information
///
/// \param ea current address
/// \return the previous address with fixup information, or BADADDR
idaman ea_t ida_export get_prev_fixup_ea(ea_t ea);
//@}
/// Get handler of standard or custom fixup
idaman const fixup_handler_t * ida_export get_fixup_handler(fixup_type_t type);
/// Use fixup information for an address.
/// This function converts item_ea flags to offsets/segments.
/// For undefined bytes, you may set item_ea == fixup_ea. In this case this
/// function will create an item (byte, word, dword) there.
/// \param item_ea start address of item to modify
/// \param fixup_ea address of fixup record
/// \param n number of operand. may be 0, 1, 2, or OPND_ALL
/// \param is_macro is the instruction at 'item_ea' a macro?
/// if yes, then partial fixups (HIGH, LOW) won't be applied
/// \retval false no fixup at fixup_ea or it has #FIXUPF_UNUSED flag
/// \retval true ok, the fixup information was applied
idaman bool ida_export apply_fixup(ea_t item_ea, ea_t fixup_ea, int n, bool is_macro);
/// Get the operand value.
/// This function get fixup bytes from data or an instruction at `ea' and
/// convert them to the operand value (maybe partially).
/// It is opposite in meaning to the `patch_fixup_value()`.
/// For example, FIXUP_HI8 read a byte at `ea' and shifts it left by 8 bits,
/// or AArch64's custom fixup BRANCH26 get low 26 bits of the insn at `ea'
/// and shifts it left by 2 bits.
/// This function is mainly used to get a relocation addend.
/// \param ea address to get fixup bytes from, the size of the fixup
/// bytes depends on the fixup type.
/// \sa fixup_handler_t::size
/// \param type fixup type
/// \retval operand value
idaman uval_t ida_export get_fixup_value(ea_t ea, fixup_type_t type);
/// Patch the fixup bytes.
/// This function updates data or an instruction at `ea' to the fixup bytes.
/// For example, FIXUP_HI8 updates a byte at `ea' to the high byte of
/// `fd->off', or AArch64's custom fixup BRANCH26 updates low 26 bits of the
/// insn at `ea' to the value of `fd->off' shifted right by 2.
/// \param ea address where data are changed, the size of the changed data
/// depends on the fixup type.
/// \sa fixup_handler_t::size
/// \param fd fixup data
/// \retval false the fixup bytes do not fit (e.g. `fd->off' is greater
/// than 0xFFFFFFC for BRANCH26). The database is changed
/// even in this case.
idaman bool ida_export patch_fixup_value(ea_t ea, const fixup_data_t &fd);
/// Get FIXUP description comment.
idaman const char *ida_export get_fixup_desc(
qstring *buf,
ea_t source,
const fixup_data_t &fd);
/// Calculate size of fixup in bytes (the number of bytes the fixup patches)
/// \retval -1 means error
idaman int ida_export calc_fixup_size(fixup_type_t type);
//--------------------------------------------------------------------------
// inline implementation
inline bool fixup_data_t::is_custom(void) const
{
return is_fixup_custom(type);
}
inline void fixup_data_t::set(ea_t source) const
{
set_fixup(source, *this);
}
inline bool fixup_data_t::get(ea_t source)
{
return get_fixup(this, source);
}
inline const char *fixup_data_t::get_desc(qstring *buf, ea_t source) const
{
return get_fixup_desc(buf, source, *this);
}
inline int fixup_data_t::calc_size() const
{
return calc_fixup_size(type);
}
inline uval_t fixup_data_t::get_value(ea_t ea) const
{
return get_fixup_value(ea, type);
}
inline bool fixup_data_t::patch_value(ea_t ea) const
{
return patch_fixup_value(ea, *this);
}
inline const fixup_handler_t *fixup_data_t::get_handler() const
{
return get_fixup_handler(type);
}
/// \name Custom fixups
/// Processor modules and plugins may register custom fixup handlers. File
/// loaders should use find_custom_fixup() function to find the handler
/// created by the processor module. The custom fixup handlers will be
/// unregistered automatically before the database gets closed.
//@{
//--------------------------------------------------------------------------
/// Implements the core behavior of a custom fixup
struct fixup_handler_t
{
int32 cbsize; ///< size of this structure
const char *name; ///< Format name, must be unique
uint32 props; ///< \ref FHF_
/// \defgroup FHF_ Fixup handler properties
/// Used by fixup_handler_t::props
//@{
#define FHF_VERIFY 0x0001 ///< verify that the value fits into WIDTH
///< bits. If this property is not set we
///< just truncate the value.
#define FHF_CODE 0x0002 ///< verify that ITEM_EA in std_apply() points
///< to an instruction.
#define FHF_FORCE_CODE 0x0004 ///< if ITEM_EA in std_apply() points to an
///< unknown item, then convert it to code.
///< this property is valid only with FHF_CODE.
#define FHF_ABS_OPVAL 0x0008 ///< create absolute refinfo in std_apply()
///< because the operand also has the absolute
///< value (usually for o_near operands)
#define FHF_SIGNED 0x0010 ///< the operand value is signed.
///< create a refinfo with REFINFO_SIGNEDOP in
///< std_apply()
//@}
/// Is the operand value signed?
bool is_signed() const { return (props & FHF_SIGNED) != 0; }
/// \defgroup fh_options Tuning options
//@{
/// The examples below show how these options work.
/// \sa std_patch_value() std_get_value()
uint8 size; ///< size in bytes
uint8 width; ///< number of significant bits before shifting
uint8 shift; ///< number of bits to shift right before patching.
///< The following should be true:
///< width - shift <= size * 8
uint8 rsrv4; // reserved
uint32 reftype; ///< reference info type and flags,
///< std_apply() produces an offset of this type
//@}
/// Apply a fixup: take it into account while analyzing the file.
/// Usually it consists of converting the operand into an offset expression.
/// \sa apply_fixup()
/// If this callback is not specified then std_apply() is used.
bool (idaapi *apply)(
const fixup_handler_t *fh,
ea_t item_ea,
ea_t fixup_ea,
int opnum,
bool is_macro,
const fixup_data_t &fd);
/// Get the operand value.
/// This callback is called from get_fixup_value().
/// \sa get_fixup_value()
/// If this callback is not specified then std_get_value() is used.
uval_t (idaapi *get_value)(const fixup_handler_t *fh, ea_t ea);
/// Patch the fixup bytes.
/// This callback is called from patch_fixup_value() or after changing the
/// fixup (e.g. after it was moved from one location to another).
/// If this callback is not specified then std_patch_value() is used.
/// \sa patch_fixup_value()
/// \retval false the fixup bytes do not fit. The database is changed
/// even in this case.
bool (idaapi *patch_value)(
const fixup_handler_t *fh,
ea_t ea,
const fixup_data_t &fd);
#ifndef SWIG
DECLARE_COMPARISONS(fixup_handler_t);
#endif
};
/// \name std_apply()
/// This internal function takes \ref fh_options and \ref FHF_ to convert
/// the fixup to an offset.
/// \name std_patch_value()
/// This internal function takes \ref fh_options and \ref FHF_ to determine
/// how to patch the bytes.
/// 1) it verifies that the fixup value fits to the fixup_handler_t::width
/// bits if the FHF_VERIFY property is specified,
/// 2) it discards bits that do not fit,
/// 3) it shifts the result right by fixup_handler_t::shift bits,
/// 4) it puts the result to the rightmost bits of fixup_handler_t::size
/// bytes at the given address.
/// For example, FIXUP_HI8 uses size = 1, and width = 16, and shift = 8, and
/// property FHF_VERIFY, or MIPS's custom fixup BRANCH26 uses size = 4,
/// and width = 28, and shift = 2.
/// In details:
/// a) size = 1, width = 16, shift = 8
/// - the value to patch is masked with 0xFFFF (width=16)
/// - then it is shifted right by 8 bits (shift=8)
/// - then the result is patched to the 8bit data at the fixup address
/// e.g.
/// 0xXX the original data
/// 0x1234 the value
/// 0x0012 the shifted value
/// 0x12 the patched data
/// b) size = 4, width = 28, shift = 2
/// - the value to patch is masked with 0xFFFFFFF (width=28)
/// - then it is shifted right by 2 bits (shift=2)
/// - then the result is patched in the low 26 bits of the 32bit
/// e.g.
/// 0x10000000 an instruction at the fixup address
/// 0x0000005C the value
/// 0x00000017 the shifted value
/// 0x10000017 the patched insn
/// \name std_get_value()
/// This internal function takes \ref fh_options to determine how to get the
/// operand value.
/// It is opposite in meaning to the std_patch_value().
/// 1) it gets the fixup_handler_t::size bytes at the given address,
/// 2) it shifts the result left by fixup_handler_t::shift bits,
/// 3) it returns the rightmost fixup_handler_t::width bits as a signed
/// value.
/// In details:
/// b) size = 4, width = 28, shift = 2
/// - it gets 4 bytes from the fixup address (the branch insn)
/// - then it shifts this dword left by 2 bits (shift=2)
/// - then the result is masked with 0xFFFFFFF (width=28)
/// e.g.
/// 0x10000017 the insn
/// 0x4000005C the unshifted value
/// 0x0000005C the masked result
/// Register a new custom fixup.
/// This function must be called by a processor module or plugin, but not
/// by a file loader. File loaders should use find_custom_fixup() function
/// to find the handler created by the processor module.
/// \return id of the new custom fixup handler with FIXUP_CUSTOM bit set or
/// 0 (e.g. when the custom fixup handler with the same name was
/// already registered).
idaman fixup_type_t ida_export register_custom_fixup(
const fixup_handler_t *cfh);
/// Unregister a new custom fixup format. Should be called by the processor
/// module before the database gets closed.
idaman bool ida_export unregister_custom_fixup(fixup_type_t type);
/// Get id of a custom fixup handler.
/// \param name name of the custom fixup handler
/// \return id with FIXUP_CUSTOM bit set or 0
idaman fixup_type_t ida_export find_custom_fixup(const char *name);
//@}
//--------------------------------------------------------------------------
/// Collect fixup records for the specified range.
/// Partially overlapping records will be reported too.
/// \return success (false means no fixup info have been found)
struct fixup_info_t
{
ea_t ea;
fixup_data_t fd;
};
DECLARE_TYPE_AS_MOVABLE(fixup_info_t);
typedef qvector<fixup_info_t> fixups_t;
idaman bool ida_export get_fixups(fixups_t *out, ea_t ea, asize_t size);
/// Does the specified address range contain any fixup information?
inline bool contains_fixups(ea_t ea, asize_t size)
{
return get_fixups(NULL, ea, size);
}
/// Relocate the bytes with fixup information once more (generic function).
/// This function may be called from loader_t::move_segm() if it suits the goal.
/// If loader_t::move_segm is not defined then this function will be called
/// automatically when moving segments or rebasing the entire program.
/// Special parameter values (from = BADADDR, size = 0, to = delta) are used
/// when the function is called from rebase_program(delta).
idaman void ida_export gen_fix_fixups(ea_t from, ea_t to, asize_t size);
/// Handle two fixups in a macro.
/// We often combine two instruction that load parts of a value into one
/// macro instruction. For example:
/// ARM: ADRP X0, #var@PAGE
/// ADD X0, X0, #var@PAGEOFF --> ADRL X0, var
/// MIPS: lui $v0, %hi(var)
/// addiu $v0, $v0, %lo(var) --> la $v0, var
/// When applying the fixups that fall inside such a macro, we should convert
/// them to one refinfo. This function does exactly that.
/// It should be called from the apply() callback of a custom fixup.
/// \return success ('false' means that RI was not changed)
idaman bool ida_export handle_fixups_in_macro(
refinfo_t *ri,
ea_t ea,
fixup_type_t other,
uint32 macro_reft_and_flags);
#endif // FIXUP_HPP

223
idasdk76/include/fpro.h Normal file
View File

@@ -0,0 +1,223 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef __FPRO_H
#define __FPRO_H
#include <pro.h>
#include <stdio.h>
/*! \file fpro.h
\brief System independent counterparts of FILE* related functions from Clib
You should not use C standard I/O functions in your modules.
The reason: Each module compiled with Borland
(and statically linked to Borland's library) will host a copy of
the FILE * information.
So, if you open a file in the plugin and pass the handle to the
kernel, the kernel will not be able to use it.
If you really need to use the standard functions, define USE_STANDARD_FILE_FUNCTIONS.
In this case do not mix them with q... functions.
*/
#if !defined(USE_STANDARD_FILE_FUNCTIONS) && !defined(__CODE_CHECKER__)
#undef stdin
#undef stdout
#undef stderr
#undef fgetc
#undef fputc
#define stdin dont_use_stdin ///< use gets()
#define stdout dont_use_stdout ///< use qprintf()
#define stderr dont_use_stderr ///< use qeprintf()
#define fopen dont_use_fopen ///< use qfopen()
#define fread dont_use_fread ///< use qfread()
#define fwrite dont_use_fwrite ///< use qfwrite()
#define ftell dont_use_ftell ///< use qftell()
#define fseek dont_use_fseek ///< use qfseek()
#define fclose dont_use_fclose ///< use qfclose()
#define fflush dont_use_fflush ///< use qflush()
#define fputc dont_use_fputc ///< use qfputc()
#define fgetc dont_use_fgetc ///< use qfgetc()
#define fgets dont_use_fgets ///< use qfgets()
#define fputs dont_use_fputs ///< use qfputs()
#define vfprintf dont_use_vfprintf ///< use qvfprintf()
#define vfscanf dont_use_vfscanf ///< use qvfscanf()
#define fprintf dont_use_fprintf ///< use qfprintf()
#define fscanf dont_use_fscanf ///< use qfscanf()
#endif
/// \name File I/O
/// The following functions work just like their counterparts from Clib,
/// only they are safer, system independent, and they set qerrno (see get_qerrno()).
//@{
idaman THREAD_SAFE FILE *ida_export qfopen(const char *file, const char *mode);
idaman THREAD_SAFE ssize_t ida_export qfread(FILE *fp, void *buf, size_t n);
idaman THREAD_SAFE ssize_t ida_export qfwrite(FILE *fp, const void *buf, size_t n);
idaman THREAD_SAFE qoff64_t ida_export qftell(FILE *fp);
idaman THREAD_SAFE int ida_export qfseek(FILE *fp, qoff64_t offset, int whence);
idaman THREAD_SAFE int ida_export qfclose(FILE *fp);
idaman THREAD_SAFE int ida_export qflush(FILE *fp);
idaman THREAD_SAFE int ida_export qfputc(int chr, FILE *fp);
idaman THREAD_SAFE int ida_export qfgetc(FILE *fp);
idaman THREAD_SAFE char *ida_export qfgets(char *s, size_t len, FILE *fp);
idaman THREAD_SAFE int ida_export qfputs(const char *s, FILE *fp);
idaman FILE *ida_export qtmpfile(void);
idaman THREAD_SAFE int ida_export qunlink(const char *fname);
idaman THREAD_SAFE int ida_export qaccess(const char *fname, int mode);
idaman THREAD_SAFE char *ida_export qgets(char *line, size_t linesize);
idaman THREAD_SAFE AS_PRINTF(2, 0) int ida_export qvfprintf(FILE *fp, const char *format, va_list va);
idaman THREAD_SAFE AS_PRINTF(1, 0) int ida_export qvprintf(const char *format, va_list va);
idaman THREAD_SAFE AS_PRINTF(1, 0) int ida_export qveprintf(const char *format, va_list va);
idaman THREAD_SAFE AS_SCANF(2, 0) int ida_export qvfscanf(FILE *fp, const char *format, va_list va);
#ifdef __cplusplus
THREAD_SAFE AS_PRINTF(2, 3) inline int qfprintf(FILE *fp, const char *format, ...)
{
va_list va;
va_start(va, format);
int code = qvfprintf(fp, format, va);
va_end(va);
return code;
}
THREAD_SAFE AS_PRINTF(1, 2) inline int qprintf(const char *format, ...)
{
va_list va;
va_start(va, format);
int code = qvprintf(format, va);
va_end(va);
return code;
}
THREAD_SAFE AS_PRINTF(1, 2) inline int qeprintf(const char *format, ...)
{
va_list va;
va_start(va, format);
int code = qveprintf(format, va);
va_end(va);
return code;
}
THREAD_SAFE AS_SCANF(2, 3) inline int qfscanf(FILE *fp, const char *format, ...)
{
va_list va;
va_start(va, format);
int code = qvfscanf(fp, format, va);
va_end(va);
return code;
}
#endif
//@}
/// Read line from file (the newline is removed from the output buffer)
/// \param buf output buffer
/// \param fp pointer to file
/// \return -1 or length of line
idaman THREAD_SAFE ssize_t ida_export qgetline(qstring *buf, FILE *fp);
/// Rename a file: 'newname' may exist, and will be deleted
idaman THREAD_SAFE int ida_export qrename(const char *oldfname, const char *newfname);
/// Move a file - more powerful version of qrename
/// \retval 0 success
/// \retval -1 system error
/// \retval else a combination of flags to be given for successful move
idaman THREAD_SAFE int ida_export qmove(const char *oldfname, const char *newfname, uint32 flags);
enum
{
QMOVE_CROSS_FS = 0x01, // UNIX: allow moving between different filesystem
QMOVE_OVERWRITE = 0x02, // Overwrite existing file
QMOVE_OVR_RO = 0x04, // Overwrite file even if it is write-protected
};
/// Copy a file.
/// \param from source file name
/// \param to destination file name
/// \param overwrite overwrite output if it exists?
/// \param cb user callback. return false to abort the copy loop
/// \param ud user data passed back to cb
/// \param flags reserved (should be zero)
/// \retval -1 input file not found
/// \retval -2 output file not writable
/// \retval -3 output file already exists while overwrite is false
/// \retval -4 write failure
/// \retval -5 interrupted from the callback
idaman THREAD_SAFE int ida_export qcopyfile(
const char *from,
const char *to,
bool overwrite = true,
bool (idaapi *cb)(uint64 pos, uint64 total, void *ud)=NULL,
void *ud = NULL,
int flags = 0);
/// Get temporary file name.
/// Returns absolute path, includes directory, and uses TEMP/TMP vars.
idaman char *ida_export qtmpnam(char *buf, size_t bufsize);
/// File janitor: will close a file at destruction-time
typedef janitor_t<FILE*> file_janitor_t;
template <> inline file_janitor_t::~janitor_t()
{
qfclose(resource);
}
/// \name readbytes/writebytes
/// Add-ins for 2..32 byte read/writes.
/// \param fp pointer to file
/// \param res value read from file
/// \param size size of value in bytes (1..32)
/// \param mostfirst is MSB first? (0/1)
/// \retval 0 All these functions return 0 on success
//@{
idaman THREAD_SAFE int ida_export freadbytes(FILE *fp,void *res,int size,int mostfirst);
idaman THREAD_SAFE int ida_export fwritebytes(FILE *fp,const void *l,int size,int mostfirst);
#ifdef __cplusplus
#define DEF_FREADBYTES(read, write, type, size) \
inline THREAD_SAFE int read(FILE *fp, type *res, bool mostfirst) \
{ return freadbytes(fp, res, size, mostfirst); } \
inline THREAD_SAFE int write(FILE *fp, const type *res, bool mostfirst) \
{ return fwritebytes(fp, res, size, mostfirst); }
DEF_FREADBYTES(fread2bytes, fwrite2bytes, int16, 2)
DEF_FREADBYTES(fread2bytes, fwrite2bytes, uint16, 2)
DEF_FREADBYTES(fread4bytes, fwrite4bytes, int32, 4)
DEF_FREADBYTES(fread4bytes, fwrite4bytes, uint32, 4)
DEF_FREADBYTES(fread8bytes, fwrite8bytes, longlong, 8)
DEF_FREADBYTES(fread8bytes, fwrite8bytes, ulonglong, 8)
#else
#define fread2bytes(fp,v,mf) freadbytes(fp,v,2,mf)
#define fwrite2bytes(fp,v,mf) fwritebytes(fp,v,2,mf)
#define fread4bytes(fp,v,mf) freadbytes(fp,v,4,mf)
#define fwrite4bytes(fp,v,mf) fwritebytes(fp,v,4,mf)
#define fread8bytes(fp,v,mf) freadbytes(fp,v,8,mf)
#define fwrite8bytes(fp,v,mf) fwritebytes(fp,v,8,mf)
#endif
//@}
#if !defined(feof) || !defined(ferror)
// If feof() and ferror() are not macros, we cannot use them
// Fortunately, for borland and vc they are macros, so there is no problem
// GCC defines them as functions: I have no idea whether they will work or not
// Anyway we remove the error directive from this file
// so the plugins can be compiled with gcc
//#error feof or ferror are not macros!
#endif
#endif

609
idasdk76/include/frame.hpp Normal file
View File

@@ -0,0 +1,609 @@
/*
* Interactive disassembler (IDA).
* Copyright (c) 1990-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
*/
#ifndef _FRAME_HPP
#define _FRAME_HPP
#include <idp.hpp>
/*! \file frame.hpp
\brief Routines to manipulate function stack frames, stack
variables, register variables and local labels.
The frame is represented as a structure:
<pre>
+------------------------------------------------+
| function arguments |
+------------------------------------------------+
| return address (isn't stored in func_t) |
+------------------------------------------------+
| saved registers (SI, DI, etc - func_t::frregs) |
+------------------------------------------------+ <- typical BP
| | |
| | | func_t::fpd
| | |
| | <- real BP
| local variables (func_t::frsize) |
| |
| |
+------------------------------------------------+ <- SP
</pre>
To access the structure of a function frame, use:
- get_struc() (use func_t::frame as structure ID)
- get_frame(const func_t *pfn)
- get_frame(ea_t ea)
*/
class struc_t;
class member_t;
class op_t;
// We need to trace value of SP register. For this we introduce
// an array of SP register change points.
// SP register change point
//
// NOTE: To manipulate/modify stack points, please use the specialized
// functions provided below in this file (stack pointer change points)
struct stkpnt_t
{
ea_t ea; // linear address
sval_t spd; // here we keep a cumulative difference from [BP-frsize]
stkpnt_t &operator=(const stkpnt_t &) = default;
DECLARE_COMPARISONS(stkpnt_t)
{
if ( ea < r.ea )
return -1;
if ( ea > r.ea )
return 1;
return 0;
}
};
DECLARE_TYPE_AS_MOVABLE(stkpnt_t);
// we declare a struct to be able to forward declare it in other files
struct stkpnts_t : public qvector<stkpnt_t>
{
DECLARE_COMPARISONS(stkpnts_t) { return compare_containers(*this, r); }
};
//--------------------------------------------------------------------------
// F R A M E M A N I P U L A T I O N
//--------------------------------------------------------------------------
/// Add function frame.
/// \param pfn pointer to function structure
/// \param frsize size of function local variables
/// \param frregs size of saved registers
/// \param argsize size of function arguments range which will be purged upon return.
/// this parameter is used for __stdcall and __pascal calling conventions.
/// for other calling conventions please pass 0.
/// \retval 1 ok
/// \retval 0 failed (no function, frame already exists)
idaman bool ida_export add_frame(
func_t *pfn,
sval_t frsize,
ushort frregs,
asize_t argsize);
/// Delete a function frame.
/// \param pfn pointer to function structure
/// \return success
idaman bool ida_export del_frame(func_t *pfn);
/// Set size of function frame.
/// Note: The returned size may not include all stack arguments. It does so
/// only for __stdcall and __fastcall calling conventions. To get the entire
/// frame size for all cases use get_struc_size(get_frame(pfn)).
/// \param pfn pointer to function structure
/// \param frsize size of function local variables
/// \param frregs size of saved registers
/// \param argsize size of function arguments that will be purged
/// from the stack upon return
/// \return success
idaman bool ida_export set_frame_size(
func_t *pfn,
asize_t frsize,
ushort frregs,
asize_t argsize);
/// Get full size of a function frame.
/// This function takes into account size of local variables + size of
/// saved registers + size of return address + number of purged bytes.
/// The purged bytes correspond to the arguments of the functions with
/// __stdcall and __fastcall calling conventions.
/// \param pfn pointer to function structure, may be NULL
/// \return size of frame in bytes or zero
idaman asize_t ida_export get_frame_size(const func_t *pfn);
/// Get size of function return address.
/// \param pfn pointer to function structure, can't be NULL
idaman int ida_export get_frame_retsize(const func_t *pfn);
/// Parts of a frame
enum frame_part_t
{
FPC_ARGS,
FPC_RETADDR,
FPC_SAVREGS,
FPC_LVARS,
};
/// Get offsets of the frame part in the frame.
/// \param range pointer to the output buffer with the frame part
/// start/end(exclusive) offsets, can't be NULL
/// \param pfn pointer to function structure, can't be NULL
/// \param part frame part
idaman void ida_export get_frame_part(range_t *range, const func_t *pfn, frame_part_t part);
/// Get starting address of arguments section
inline ea_t frame_off_args(const func_t *pfn)
{
range_t range;
get_frame_part(&range, pfn, FPC_ARGS);
return range.start_ea;
}
/// Get starting address of return address section
inline ea_t frame_off_retaddr(const func_t *pfn)
{
range_t range;
get_frame_part(&range, pfn, FPC_RETADDR);
return range.start_ea;
}
/// Get starting address of saved registers section
inline ea_t frame_off_savregs(const func_t *pfn)
{
range_t range;
get_frame_part(&range, pfn, FPC_SAVREGS);
return range.start_ea;
}
/// Get start address of local variables section
inline ea_t frame_off_lvars(const func_t *pfn)
{
range_t range;
get_frame_part(&range, pfn, FPC_LVARS);
return range.start_ea;
}
/// Does the given offset lie within the arguments section?
inline bool processor_t::is_funcarg_off(const func_t *pfn, uval_t frameoff) const
{
range_t args;
get_frame_part(&args, pfn, FPC_ARGS);
return stkup()
? frameoff < args.end_ea
: frameoff >= args.start_ea;
}
/// Does the given offset lie within the local variables section?
inline sval_t processor_t::lvar_off(const func_t *pfn, uval_t frameoff) const
{
range_t lvars;
get_frame_part(&lvars, pfn, FPC_LVARS);
return stkup()
? frameoff - lvars.start_ea
: lvars.end_ea - frameoff;
}
/// Get pointer to function frame.
/// \param pfn pointer to function structure
idaman struc_t *ida_export get_frame(const func_t *pfn);
/// Get pointer to function frame.
/// \param ea any address in the function
inline struc_t *get_frame(ea_t ea) { return get_frame(get_func(ea)); }
/// Convert struct offsets into fp-relative offsets.
/// This function converts the offsets inside the struc_t object
/// into the frame pointer offsets (for example, EBP-relative).
inline sval_t soff_to_fpoff(func_t *pfn, uval_t soff)
{
return soff - pfn->frsize + pfn->fpd;
}
/// Update frame pointer delta.
/// \param pfn pointer to function structure
/// \param fpd new fpd value.
/// cannot be bigger than the local variable range size.
/// \return success
idaman bool ida_export update_fpd(func_t *pfn, asize_t fpd);
/// Set the number of purged bytes for a function or data item (funcptr).
/// This function will update the database and plan to reanalyze items
/// referencing the specified address. It works only for processors
/// with #PR_PURGING bit in 16 and 32 bit modes.
/// \param ea address of the function of item
/// \param nbytes number of purged bytes
/// \param override_old_value may overwrite old information about purged bytes
/// \return success
idaman bool ida_export set_purged(ea_t ea, int nbytes, bool override_old_value);
/// Get function by its frame id.
/// \warning this function works only with databases created by IDA > 5.6
/// \param frame_id id of the function frame
/// \return start address of the function or #BADADDR
idaman ea_t ida_export get_func_by_frame(tid_t frame_id);
//--------------------------------------------------------------------------
// S T A C K V A R I A B L E S
//--------------------------------------------------------------------------
/// Get pointer to stack variable.
/// \param actval actual value used to fetch stack variable
/// this pointer may point to 'v'
/// \param insn the instruction
/// \param x reference to instruction operand
/// \param v immediate value in the operand (usually x.addr)
/// \return NULL or ptr to stack variable
idaman member_t *ida_export get_stkvar(
sval_t *actval,
const insn_t &insn,
const op_t &x,
sval_t v);
/// Automatically add stack variable if doesn't exist.
/// Processor modules should use insn_t::create_stkvar().
/// \param insn the instruction
/// \param x reference to instruction operand
/// \param v immediate value in the operand (usually x.addr)
/// \param flags \ref STKVAR_1
/// \return success
idaman bool ida_export add_stkvar(const insn_t &insn, const op_t &x, sval_t v, int flags);
/// \defgroup STKVAR_1 Add stkvar flags
/// Passed as 'flags' parameter to add_stkvar()
//@{
#define STKVAR_VALID_SIZE 0x0001 ///< x.dtyp contains correct variable type
///< (for insns like 'lea' this bit must be off)
///< in general, dr_O references do not allow
///< to determine the variable size
//@}
/// Define/redefine a stack variable.
/// \param pfn pointer to function
/// \param name variable name, NULL means autogenerate a name
/// \param off offset of the stack variable in the frame.
/// negative values denote local variables, positive - function arguments.
/// \param flags variable type flags (byte_flag() for a byte variable, for example)
/// \param ti additional type information (like offsets, structs, etc)
/// \param nbytes number of bytes occupied by the variable
/// \return success
idaman bool ida_export define_stkvar(
func_t *pfn,
const char *name,
sval_t off,
flags_t flags,
const opinfo_t *ti,
asize_t nbytes);
/// Build automatic stack variable name.
/// \param buf pointer to buffer
/// \param pfn pointer to function (can't be NULL!)
/// \param v value of variable offset
/// \return length of stack variable name or -1
idaman ssize_t ida_export build_stkvar_name(
qstring *buf,
const func_t *pfn,
sval_t v);
/// Calculate offset of stack variable in the frame structure.
/// \param pfn pointer to function (can't be NULL!)
/// \param insn the instruction
/// \param n number of operand: (0..#UA_MAXOP-1)
/// -1 if error, return #BADADDR
/// \return #BADADDR if some error (issue a warning if stack frame is bad)
idaman ea_t ida_export calc_stkvar_struc_offset(
func_t *pfn,
const insn_t &insn,
int n);
/// Find and delete wrong frame info.
/// Namely, we delete:
/// - unreferenced stack variable definitions
/// - references to dead stack variables (i.e. operands displayed in red)
/// these operands will be untyped and most likely displayed in hex.
/// We also plan to reanalyze instruction with the stack frame references
/// \param pfn pointer to the function
/// \param should_reanalyze callback to determine which instructions to reanalyze
/// \return number of deleted definitions
idaman int ida_export delete_wrong_frame_info(
func_t *pfn,
bool idaapi should_reanalyze(const insn_t &insn));
//--------------------------------------------------------------------------
// R E G I S T E R V A R I A B L E S
//--------------------------------------------------------------------------
/// \defgroup regvar Register variables
/// Definition of ::regvar_t and related functions
//@{
idaman void ida_export free_regvar(struct regvar_t *v);
/// A register variable allows the user to rename a general processor register
/// to a meaningful name.
/// IDA doesn't check whether the target assembler supports the register renaming.
/// All register definitions will appear at the beginning of the function.
struct regvar_t : public range_t
{
char *canon = nullptr; ///< canonical register name (case-insensitive)
char *user = nullptr; ///< user-defined register name
char *cmt = nullptr; ///< comment to appear near definition
regvar_t() {}
regvar_t(const regvar_t &r) : range_t(r)
{
canon = ::qstrdup(r.canon);
user = ::qstrdup(r.user);
cmt = ::qstrdup(r.cmt);
}
~regvar_t() { free_regvar(this); }
regvar_t &operator=(const regvar_t &r)
{
if ( this != &r )
{
free_regvar(this);
new (this) regvar_t(r);
}
return *this;
}
void swap(regvar_t &r)
{
uchar buf[sizeof(*this)];
memcpy(buf, &r, sizeof(buf));
memcpy(&r, this, sizeof(buf));
memcpy(this, buf, sizeof(buf));
}
#ifndef SWIG
DECLARE_COMPARISONS(regvar_t);
#endif
};
DECLARE_TYPE_AS_MOVABLE(regvar_t);
/// Define a register variable.
/// \param pfn function in which the definition will be created
/// \param ea1,ea2 range of addresses within the function where the definition
/// will be used
/// \param canon name of a general register
/// \param user user-defined name for the register
/// \param cmt comment for the definition
/// \return \ref REGVAR_ERROR_
idaman int ida_export add_regvar(
func_t *pfn,
ea_t ea1,
ea_t ea2,
const char *canon,
const char *user,
const char *cmt);
/// \defgroup REGVAR_ERROR_ Register variable error codes
/// Return values for functions in described in \ref regvar
//@{
#define REGVAR_ERROR_OK 0 ///< all ok
#define REGVAR_ERROR_ARG (-1) ///< function arguments are bad
#define REGVAR_ERROR_RANGE (-2) ///< the definition range is bad
#define REGVAR_ERROR_NAME (-3) ///< the provided name(s) can't be accepted
//@}
/// Find a register variable definition (powerful version).
/// One of 'canon' and 'user' should be NULL.
/// If both 'canon' and 'user' are NULL it returns the first regvar
/// definition in the range.
/// \param pfn function in question
/// \param ea1,ea2 range of addresses to search.
/// ea1==BADADDR means the entire function
/// \param canon name of a general register
/// \param user user-defined name for the register
/// \return NULL-not found, otherwise ptr to regvar_t
idaman regvar_t *ida_export find_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon, const char *user);
/// Find a register variable definition.
/// \param pfn function in question
/// \param ea current address
/// \param canon name of a general register
/// \return NULL-not found, otherwise ptr to regvar_t
inline regvar_t *find_regvar(func_t *pfn, ea_t ea, const char *canon)
{
return find_regvar(pfn, ea, ea+1, canon, NULL);
}
/// Is there a register variable definition?
/// \param pfn function in question
/// \param ea current address
inline bool has_regvar(func_t *pfn, ea_t ea)
{
return find_regvar(pfn, ea, ea+1, NULL, NULL) != NULL;
}
/// Rename a register variable.
/// \param pfn function in question
/// \param v variable to rename
/// \param user new user-defined name for the register
/// \return \ref REGVAR_ERROR_
idaman int ida_export rename_regvar(func_t *pfn, regvar_t *v, const char *user);
/// Set comment for a register variable.
/// \param pfn function in question
/// \param v variable to rename
/// \param cmt new comment
/// \return \ref REGVAR_ERROR_
idaman int ida_export set_regvar_cmt(func_t *pfn, regvar_t *v, const char *cmt);
/// Delete a register variable definition.
/// \param pfn function in question
/// \param ea1,ea2 range of addresses within the function where the definition holds
/// \param canon name of a general register
/// \return \ref REGVAR_ERROR_
idaman int ida_export del_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon);
//@} regvar
//--------------------------------------------------------------------------
// S P R E G I S T E R C H A N G E P O I N T S
//--------------------------------------------------------------------------
/// Add automatic SP register change point.
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address where SP changes.
/// usually this is the end of the instruction which
/// modifies the stack pointer (\cmd{ea}+\cmd{size})
/// \param delta difference between old and new values of SP
/// \return success
idaman bool ida_export add_auto_stkpnt(func_t *pfn, ea_t ea, sval_t delta);
/// Add user-defined SP register change point.
/// \param ea linear address where SP changes
/// \param delta difference between old and new values of SP
/// \return success
idaman bool ida_export add_user_stkpnt(ea_t ea, sval_t delta);
/// Delete SP register change point.
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address
/// \return success
idaman bool ida_export del_stkpnt(func_t *pfn, ea_t ea);
/// Get difference between the initial and current values of ESP.
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address of an instruction
/// \return 0 or the difference, usually a negative number.
/// returns the sp-diff before executing the instruction.
idaman sval_t ida_export get_spd(func_t *pfn, ea_t ea);
/// Get effective difference between the initial and current values of ESP.
/// This function returns the sp-diff used by the instruction.
/// The difference between get_spd() and get_effective_spd() is present only
/// for instructions like "pop [esp+N]": they modify sp and use the modified value.
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address
/// \return 0 or the difference, usually a negative number
idaman sval_t ida_export get_effective_spd(func_t *pfn, ea_t ea);
/// Get modification of SP made at the specified location
/// \param pfn pointer to function. may be NULL.
/// \param ea linear address
/// \return 0 if the specified location doesn't contain a SP change point.
/// otherwise return delta of SP modification.
idaman sval_t ida_export get_sp_delta(func_t *pfn, ea_t ea);
/// Recalculate SP delta for an instruction that stops execution.
/// The next instruction is not reached from the current instruction.
/// We need to recalculate SP for the next instruction.
///
/// This function will create a new automatic SP register change
/// point if necessary. It should be called from the emulator (emu.cpp)
/// when auto_state == ::AU_USED if the current instruction doesn't pass
/// the execution flow to the next instruction.
/// \param cur_ea linear address of the current instruction
/// \retval 1 new stkpnt is added
/// \retval 0 nothing is changed
idaman bool ida_export recalc_spd(ea_t cur_ea);
/// An xref to an argument or variable located in a function's stack frame
struct xreflist_entry_t
{
ea_t ea; ///< Location of the insn referencing the stack frame member
uchar opnum; ///< Number of the operand of that instruction
uchar type; ///< The type of xref (::cref_t & ::dref_t)
DECLARE_COMPARISONS(xreflist_entry_t)
{
int code = ::compare(ea, r.ea);
if ( code == 0 )
{
code = ::compare(type, r.type);
if ( code == 0 )
code = ::compare(opnum, r.opnum);
}
return code;
}
};
DECLARE_TYPE_AS_MOVABLE(xreflist_entry_t);
typedef qvector<xreflist_entry_t> xreflist_t; ///< vector of xrefs to variables in a function's stack frame
/// Fill 'out' with a list of all the xrefs made from function 'pfn', to
/// the argument or variable 'mptr' in 'pfn's stack frame.
/// \param out the list of xrefs to fill.
/// \param pfn the function to scan.
/// \param mptr the argument/variable in pfn's stack frame.
idaman void ida_export build_stkvar_xrefs(xreflist_t *out, func_t *pfn, const member_t *mptr);
#ifndef NO_OBSOLETE_FUNCS
idaman DEPRECATED ea_t ida_export get_min_spd_ea(func_t *pfn);
idaman DEPRECATED int ida_export delete_unreferenced_stkvars(func_t *pfn);
idaman DEPRECATED int ida_export delete_wrong_stkvar_ops(func_t *pfn);
#endif
#endif // _FRAME_HPP

Some files were not shown because too many files have changed in this diff Show More