update to ida 7.6, add builds
This commit is contained in:
@@ -29,27 +29,27 @@
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug64|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release64|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -179,7 +179,8 @@
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<OpenMPSupport>false</OpenMPSupport>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)idasdk75\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)idasdk76\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp14</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
@@ -191,7 +192,7 @@
|
||||
<AdditionalOptions>/EXPORT:PLUGIN %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>false</DataExecutionPrevention>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)idasdk75\lib\x64_win_vc_64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)idasdk76\lib\x64_win_vc_64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<ProjectReference>
|
||||
<LinkLibraryDependencies>false</LinkLibraryDependencies>
|
||||
|
||||
BIN
builds/Release64/SigMaker64.dll
Normal file
BIN
builds/Release64/SigMaker64.dll
Normal file
Binary file not shown.
@@ -1,823 +0,0 @@
|
||||
#
|
||||
# Common part of make files for IDA.
|
||||
#
|
||||
|
||||
# find directory of allmake.mak:
|
||||
IDA:=$(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
# define the version number we are building
|
||||
IDAVER_MAJOR:=7
|
||||
IDAVER_MINOR:=5
|
||||
# 750
|
||||
IDAVERDECIMAL:=$(IDAVER_MAJOR)$(IDAVER_MINOR)0
|
||||
# 7.5
|
||||
IDAVERDOTTED:=$(IDAVER_MAJOR).$(IDAVER_MINOR)
|
||||
|
||||
# only 32-bit cygwin make is support on Windows
|
||||
ifeq ($(OS),Windows_NT)
|
||||
ifneq ($(MAKE_HOST),i686-pc-cygwin)
|
||||
$(error Only 32-bit cygwin make is supported on Windows. Make sure you are not using 64-bit cygwin, msys, msys2, or any other version of win32 make)
|
||||
endif
|
||||
endif
|
||||
|
||||
# if no targets are defined, default to host OS
|
||||
ifeq ($(or $(__ANDROID__),$(__ANDROID_X86__),$(__ARMLINUX__),$(__LINUX__),$(__MAC__),$(__NT__)),)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
__NT__=1
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
__LINUX__=1
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
__MAC__=1
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# only one build target may be defined
|
||||
ifneq ($(__ANDROID__)$(__ANDROID_X86__)$(__ARMLINUX__)$(__LINUX__)$(__MAC__)$(__NT__),1)
|
||||
$(error Only one build target may be defined (__ANDROID__, __ANDROID_X86__, __ARMLINUX__, __LINUX__, __MAC__, or __NT__))
|
||||
endif
|
||||
|
||||
# detect build configuration
|
||||
# Note: will set one of M, MM, MMH, M32, MO, MMO, MMHO, MO32, MSO, MMSO, or MSO32
|
||||
BUILD_CONFIG-1 := M
|
||||
BUILD_CONFIG-$(__EA64__) += M
|
||||
BUILD_CONFIG-$(USE_STATIC_RUNTIME) += S
|
||||
BUILD_CONFIG-$(IDAHOME) += H
|
||||
BUILD_CONFIG-$(NDEBUG) += O
|
||||
BUILD_CONFIG-$(__X86__) += 32
|
||||
empty :=
|
||||
space := $(empty) $(empty)
|
||||
comma := ,
|
||||
BUILD_CONFIG := $(subst $(space),,$(BUILD_CONFIG-1))
|
||||
$(BUILD_CONFIG) := 1
|
||||
|
||||
# definition of a single \n character (empty lines are important!)
|
||||
define newline
|
||||
|
||||
|
||||
endef
|
||||
|
||||
# disable x86 ida64 builds
|
||||
ifeq ($(or $(MM32),$(MMO32),$(MMSO32)),1)
|
||||
$(error x86 ida64 builds have been disabled)
|
||||
endif
|
||||
|
||||
ifdef __ARM__
|
||||
PROCDEF = __ARM__
|
||||
TARGET_PROCESSOR_NAME=arm
|
||||
else ifndef __X86__
|
||||
ARCH_FLAGS = -m64
|
||||
TARGET_PROCESSOR_NAME=x64
|
||||
else
|
||||
ARCH_FLAGS = -m32
|
||||
TARGET_PROCESSOR_NAME=x86
|
||||
endif
|
||||
|
||||
# define some variables to simplify build system
|
||||
ifndef __X86__
|
||||
__X64__ = 1
|
||||
ifndef __EA64__
|
||||
__X32__ = 1
|
||||
endif
|
||||
endif
|
||||
ifndef __NT__
|
||||
__UNIX__ = 1
|
||||
endif
|
||||
|
||||
ifndef IDAHOME
|
||||
IDAADV = 1
|
||||
endif
|
||||
|
||||
# define SYSNAME
|
||||
SYSNAME-$(__LINUX__) = linux
|
||||
SYSNAME-$(__MAC__) = mac
|
||||
SYSNAME-$(__NT__) = win
|
||||
SYSNAME = $(SYSNAME-1)
|
||||
|
||||
# path functions (depending on host OS)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
# define: convert unix path to dos path by replacing slashes by backslashes
|
||||
dospath=$(subst /,\\,$(1))
|
||||
else
|
||||
# define: dospath does not do anything in unix
|
||||
dospath=$(1)
|
||||
endif
|
||||
# define: return 1 if path exists, 0 otherwise
|
||||
ls=$(if $(wildcard $(1)),1,0)
|
||||
|
||||
# define: logical negation
|
||||
not = $(if $(1),,1)
|
||||
|
||||
# define: greater or equal
|
||||
gte = $(if $(filter-out $(1),$(word 2,$(sort $(1) $(2)))),,1)
|
||||
|
||||
include $(IDA)defaults.mk
|
||||
|
||||
#############################################################################
|
||||
ifdef __NT__
|
||||
COMPILER_NAME=vc
|
||||
|
||||
# Visual C++ Toolchain and Windows SDK paths
|
||||
# Note: see comments in defaults.mk for more information about these
|
||||
# variables.
|
||||
|
||||
# This function searches for a specified path, converts it to a 8.3
|
||||
# path with forward slashes as separator, and exports it as an
|
||||
# environment variable. This way, subcalls to make do not need to
|
||||
# call $(shell) again.
|
||||
define require_path
|
||||
$$(if $(strip $$($(1))),,$$(eval $(1):=$$(subst \,/,$$(shell cygpath -d $(2) 2>/dev/null))))
|
||||
$$(if $(strip $$($(1))),,$$(error Could not find $(3) in $(2)$$(newline)*** See defaults.mk and "Visual C++ Toolchain and Windows SDK paths" in allmake.mak))
|
||||
$$(eval export $(1))
|
||||
endef
|
||||
|
||||
# This function fixes variables imported from defaults.mk/vcvars.bat
|
||||
# by ensuring that they are surrounded by quotes and by removing the
|
||||
# trailing backslash.
|
||||
fix_var=$(1):='$$(patsubst %\,%,$$(patsubst '%,%,$$(patsubst %',%,$$(patsubst "%,%,$$(patsubst %",%,$$($(1)))))))'
|
||||
|
||||
# Note: these cfg files are created in makeenv_vc.mak
|
||||
ifdef __XPCOMPAT__
|
||||
-include $(IDA)vs17paths_xp.cfg
|
||||
else
|
||||
-include $(IDA)vs17paths.cfg
|
||||
endif
|
||||
|
||||
# Visual C++ 2017 Install Directory
|
||||
ifndef MSVC_ROOT
|
||||
ifneq (,$(findstring Microsoft$(space)Visual$(space)Studio$(space),$(VCINSTALLDIR)))
|
||||
ifeq (,$(findstring 2017,$(VCINSTALLDIR)))
|
||||
$(error Please check your system environment variable VCInstallDir [$(VCINSTALLDIR)].$(newline)It seems to be pointing to an old version of Visual Studio (and not version 2017).$(newline)You may override it in defaults.mk.)
|
||||
endif
|
||||
endif
|
||||
$(eval $(call fix_var,VCINSTALLDIR))
|
||||
$(eval $(call require_path,MSVC_ROOT,$(VCINSTALLDIR),Visual C++ 2017 Install Directory))
|
||||
export MSVC_ROOT
|
||||
endif
|
||||
|
||||
# Visual C++ 2017 Tools Version
|
||||
ifndef MSVC_TOOLSVER
|
||||
ifndef VCToolsVersion
|
||||
# Try to obtain version from Microsoft.VCToolsVersion.default.txt
|
||||
MSVC_TOOLSVER_PATH = $(MSVC_ROOT)/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt
|
||||
VCToolsVersion := $(shell cat $(MSVC_TOOLSVER_PATH) 2> /dev/null)
|
||||
ifeq (,$(VCToolsVersion))
|
||||
# If that failed, try to detect latest version from the directory names
|
||||
VCToolsVersion := $(notdir $(lastword $(sort $(wildcard $(MSVC_ROOT)/Tools/MSVC/14.*))))
|
||||
endif
|
||||
ifeq (,$(VCToolsVersion))
|
||||
$(error Could not find Visual C++ 2017 Tools Version in $(MSVC_TOOLSVER_PATH))
|
||||
endif
|
||||
endif
|
||||
$(eval $(call fix_var,VCToolsVersion))
|
||||
MSVC_TOOLSVER := $(VCToolsVersion)
|
||||
export MSVC_TOOLSVER
|
||||
endif
|
||||
|
||||
# Final Visual C++ 2017 Tools path
|
||||
$(eval $(call require_path,MSVC_PATH,$(MSVC_ROOT)/Tools/MSVC/$(MSVC_TOOLSVER),Visual C++ 2017 Tools))
|
||||
|
||||
MSVC_BIN-X86 ?= $(MSVC_PATH)/bin/HostX86/x86
|
||||
MSVC_BIN-X64 ?= $(MSVC_PATH)/bin/HostX64/x64
|
||||
ifdef __X86__
|
||||
MSVC_BIN ?= $(MSVC_BIN-X86)
|
||||
else
|
||||
MSVC_BIN ?= $(MSVC_BIN-X64)
|
||||
endif
|
||||
MSVC_INCLUDE ?= $(MSVC_PATH)/Include
|
||||
|
||||
# Windows SDK Install Directory
|
||||
ifndef WSDK_PATH
|
||||
$(eval $(call fix_var,WindowsSdkDir))
|
||||
$(eval $(call require_path,WSDK_PATH,$(WindowsSdkDir),Windows SDK Install Directory))
|
||||
export WSDK_PATH
|
||||
endif
|
||||
|
||||
# Windows SDK Version
|
||||
ifndef WSDK_VER
|
||||
ifndef WindowsSDKVersion
|
||||
# Detect the latest version of the Windows SDK
|
||||
WSDK_VER_PATH = $(WSDK_PATH)/Include/10.*
|
||||
WindowsSDKVersion := $(notdir $(lastword $(sort $(wildcard $(WSDK_VER_PATH)))))
|
||||
ifeq (,$(WindowsSDKVersion))
|
||||
$(error Could not find Windows SDK Version in $(WSDK_VER_PATH))
|
||||
endif
|
||||
endif
|
||||
$(eval $(call fix_var,WindowsSDKVersion))
|
||||
WSDK_VER := $(WindowsSDKVersion)
|
||||
export WSDK_VER
|
||||
endif
|
||||
|
||||
# Windows SDK Include/Lib paths
|
||||
INCLUDE_UCRT_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/ucrt
|
||||
LIB_UCRT_PATH ?= $(WSDK_PATH)/Lib/$(WSDK_VER)/ucrt
|
||||
$(eval $(call require_path,INCLUDE_UCRT,$(INCLUDE_UCRT_PATH),Windows SDK Include/ucrt))
|
||||
$(eval $(call require_path,LIB_UCRT,$(LIB_UCRT_PATH),Windows SDK Lib/ucrt))
|
||||
|
||||
ifdef __XPCOMPAT__
|
||||
$(eval $(call require_path,INCLUDE_MSSDK71,$(MSSDK71_PATH)/Include,Microsoft SDK Include))
|
||||
$(eval $(call require_path,LIB_MSSDK71,$(MSSDK71_PATH)/Lib,Microsoft SDK Lib))
|
||||
$(eval $(call require_path,SDK_BIN,$(MSSDK71_PATH)/Bin,Microsoft SDK Bin))
|
||||
else
|
||||
INCLUDE_SHARED_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/shared
|
||||
INCLUDE_UM_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/um
|
||||
LIB_UM_PATH ?= $(WSDK_PATH)/Lib/$(WSDK_VER)/um
|
||||
SDK_BIN_PATH ?= $(WSDK_PATH)/Bin/$(WSDK_VER)/
|
||||
|
||||
$(eval $(call require_path,INCLUDE_SHARED,$(INCLUDE_SHARED_PATH),Windows SDK Include/shared))
|
||||
$(eval $(call require_path,INCLUDE_UM,$(INCLUDE_UM_PATH),Windows SDK Include/um))
|
||||
$(eval $(call require_path,LIB_UM,$(LIB_UM_PATH),Windows SDK Lib/um))
|
||||
$(eval $(call require_path,SDK_BIN,$(SDK_BIN_PATH),Windows SDK Bin))
|
||||
endif
|
||||
|
||||
# Export INCLUDE as an environment variable so it may be used by cl.
|
||||
ifndef INCLUDE
|
||||
ifdef __XPCOMPAT__
|
||||
INCLUDE = $(MSVC_INCLUDE);$(INCLUDE_UCRT);$(INCLUDE_MSSDK71)
|
||||
else
|
||||
INCLUDE = $(MSVC_INCLUDE);$(INCLUDE_UCRT);$(INCLUDE_UM);$(INCLUDE_SHARED)
|
||||
endif
|
||||
export INCLUDE
|
||||
endif
|
||||
|
||||
# Export LIB as an environment variable so it may be used by cl/link.
|
||||
ifndef LIB
|
||||
ifdef __XPCOMPAT__
|
||||
ifdef __X86__
|
||||
LIB = $(MSVC_PATH)/lib/x86;$(LIB_UCRT)/x86;$(LIB_MSSDK71)
|
||||
else
|
||||
LIB = $(MSVC_PATH)/lib/x64;$(LIB_UCRT)/x64;$(LIB_MSSDK71)/x64
|
||||
endif
|
||||
else
|
||||
ifdef __X86__
|
||||
LIB = $(MSVC_PATH)/lib/x86;$(LIB_UCRT)/x86;$(LIB_UM)/x86
|
||||
else
|
||||
LIB = $(MSVC_PATH)/lib/x64;$(LIB_UCRT)/x64;$(LIB_UM)/x64
|
||||
endif
|
||||
endif
|
||||
export LIB
|
||||
endif
|
||||
|
||||
# If a Visual Studio Command Prompt is used, make sure the target
|
||||
# architecture is correct.
|
||||
ifdef VSCMD_ARG_TGT_ARCH
|
||||
ifneq ($(VSCMD_ARG_TGT_ARCH),$(TARGET_PROCESSOR_NAME))
|
||||
ifdef __X86__
|
||||
EXPECTED_ARCH = x86
|
||||
else
|
||||
EXPECTED_ARCH = x64
|
||||
endif
|
||||
LOWERCASE_BUILD_CONFIG := $(subst M,m,$(subst S,s,$(subst O,o,$(BUILD_CONFIG))))
|
||||
$(error Please use the correct Visual Studio Command Prompt for the target architecture$(newline)*** The target architecture for '$(LOWERCASE_BUILD_CONFIG)' is $(EXPECTED_ARCH), and the architecture for the current Visual Studio Command Prompt is $(VSCMD_ARG_TGT_ARCH)))
|
||||
endif
|
||||
endif
|
||||
#############################################################################
|
||||
else ifdef __LINUX__
|
||||
COMPILER_NAME=gcc
|
||||
PTHR_SWITCH=-pthread
|
||||
STDLIBS += -lrt -lpthread -lc
|
||||
#############################################################################
|
||||
else ifdef __MAC__
|
||||
COMPILER_NAME=clang
|
||||
STDLIBS += -lpthread -liconv
|
||||
ARCH_FLAGS-$(__X64__) = -arch x86_64
|
||||
ARCH_FLAGS-$(__X86__) = -arch i386
|
||||
ARCH_FLAGS += $(ARCH_FLAGS-1)
|
||||
# The following value is defined in defaults.mk.
|
||||
ARCH_FLAGS += -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET)
|
||||
ifndef MACSDK
|
||||
MACSDK := $(shell /usr/bin/xcrun --sdk macosx --show-sdk-path)
|
||||
ifeq ($(MACSDK),)
|
||||
$(error Could not find MacOSX SDK)
|
||||
endif
|
||||
export MACSDK
|
||||
endif
|
||||
ARCH_FLAGS += -isysroot $(MACSDK)
|
||||
endif
|
||||
|
||||
#############################################################################
|
||||
# toolchain-specific variables
|
||||
|
||||
ifneq (,$(filter $(COMPILER_NAME),gcc clang))
|
||||
# file extensions
|
||||
A = .a
|
||||
B = $(SUFF64)
|
||||
O = .o
|
||||
II = .i
|
||||
# toolchain output switches
|
||||
OBJSW = -o # with space
|
||||
OUTAR =
|
||||
OUTII = -o # with space
|
||||
OUTSW = -o # with space
|
||||
ifdef __MAC__
|
||||
OUTMAP = -Wl,-map,
|
||||
else
|
||||
OUTMAP = -Wl,-Map,
|
||||
endif
|
||||
# misc switches
|
||||
AROPT = rc
|
||||
CPPONLY = -E
|
||||
FORCEC = -xc
|
||||
NORTTI = -fno-rtti
|
||||
ifdef __MAC__
|
||||
OUTDLL = -dynamiclib
|
||||
else
|
||||
OUTDLL = --shared
|
||||
endif
|
||||
# utilities
|
||||
CCACHE-$(USE_CCACHE) = ccache
|
||||
ifeq ($(COMPILER_NAME),clang)
|
||||
_CC = clang
|
||||
_CXX = clang++
|
||||
else
|
||||
_CC = gcc
|
||||
_CXX = g++
|
||||
ifdef USE_GOLD
|
||||
GOLD = -fuse-ld=gold
|
||||
endif
|
||||
endif
|
||||
AR = $(CROSS_PREFIX)ar$(HOST_EXE) $(AROPT)
|
||||
CC = $(CCACHE-1) $(CROSS_PREFIX)$(_CC)$(HOST_EXE) $(ARCH_FLAGS)
|
||||
CCL = $(CROSS_PREFIX)$(_CXX)$(HOST_EXE) $(ARCH_FLAGS) $(GOLD)
|
||||
CXX = $(CCACHE-1) $(CROSS_PREFIX)$(_CXX)$(HOST_EXE) $(ARCH_FLAGS)
|
||||
else ifeq ($(COMPILER_NAME),vc)
|
||||
# file extensions
|
||||
A = .lib
|
||||
B = $(SUFF64).exe
|
||||
O = .obj
|
||||
II = .i
|
||||
# toolchain output switches
|
||||
OBJSW = /Fo
|
||||
OUTAR = /OUT:
|
||||
OUTII = /Fi
|
||||
OUTSW = /OUT:
|
||||
OUTMAP = /map:
|
||||
# misc switches
|
||||
CPPONLY = /P
|
||||
FORCEC = /TC
|
||||
NOLOGO = /nologo
|
||||
NORTTI = /GR-
|
||||
OUTDLL = /DLL
|
||||
# utilities
|
||||
AR = $(MSVC_BIN)/lib.exe $(NOLOGO)
|
||||
CC = $(MSVC_BIN)/cl.exe $(NOLOGO)
|
||||
CCL = $(MSVC_BIN)/link.exe $(NOLOGO)
|
||||
CXX = $(CC)
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# target-specific cflags/ldflags
|
||||
ifneq (,$(filter $(COMPILER_NAME),gcc clang))
|
||||
|
||||
# system cflags
|
||||
CC_DEFS += $(PROCDEF)
|
||||
ifdef __MAC__
|
||||
CC_DEFS += __MAC__
|
||||
else
|
||||
CC_DEFS += __LINUX__
|
||||
endif
|
||||
|
||||
# pic-related flags
|
||||
# Note: this variable may be overridden in other parts of the build
|
||||
PIC = -fPIC
|
||||
|
||||
ifdef __MAC__
|
||||
LDPIE = $(PIC) -Wl,-pie
|
||||
else
|
||||
LDPIE = $(PIC) -pie
|
||||
endif
|
||||
|
||||
# common cflags
|
||||
CC_DEFS += $(DEF64)
|
||||
CC_DEFS += $(DEFX86)
|
||||
|
||||
CC_F += $(PIC)
|
||||
CC_F += -fdiagnostics-show-option
|
||||
CC_F += -fno-strict-aliasing
|
||||
CC_F += -fvisibility=hidden
|
||||
CC_F += -fwrapv
|
||||
|
||||
CC_INCP += $(I)
|
||||
|
||||
CC_W += -Wall
|
||||
CC_W += -Wextra
|
||||
CC_W += -Wformat=2
|
||||
CC_W += -Werror=format-security
|
||||
CC_W += -Werror=format-nonliteral
|
||||
CC_W += -Wshadow
|
||||
CC_W += -Wunused
|
||||
|
||||
CC_WNO += -Wno-format-y2k
|
||||
CC_WNO += -Wno-missing-field-initializers
|
||||
CC_WNO += -Wno-sign-compare
|
||||
|
||||
CC_X += -g
|
||||
CC_X += -pipe
|
||||
|
||||
# enable c++11
|
||||
CXXSTD = -std=c++11
|
||||
|
||||
CXX_F += -fvisibility-inlines-hidden
|
||||
CXX_WNO += -Wno-invalid-offsetof
|
||||
|
||||
# system-specific cflags
|
||||
ifeq ($(COMPILER_NAME),clang) # mac/android
|
||||
# 'cc -dumpversion' always reports 4.2.1 for clang
|
||||
# https://stackoverflow.com/questions/12893731/why-does-clang-dumpversion-report-4-2-1
|
||||
|
||||
# clang is extra picky - need to add some warning supressions
|
||||
# must eventually get rid of most of these
|
||||
CC_WNO += -Wno-char-subscripts
|
||||
CC_WNO += -Wno-dynamic-class-memaccess
|
||||
CC_WNO += -Wno-int-to-pointer-cast
|
||||
CC_WNO += -Wno-invalid-source-encoding
|
||||
CC_WNO += -Wno-logical-not-parentheses
|
||||
CC_WNO += -Wno-logical-op-parentheses
|
||||
CC_WNO += -Wno-null-conversion
|
||||
CC_WNO += -Wno-nullability-completeness
|
||||
CC_WNO += -Wno-parentheses-equality
|
||||
CC_WNO += -Wno-self-assign
|
||||
CC_WNO += -Wno-unused-const-variable
|
||||
CC_WNO += -Wno-unused-function
|
||||
CC_WNO += -Wno-unused-private-field
|
||||
CC_WNO += -Wno-unused-variable
|
||||
CC_WNO += -Wno-varargs
|
||||
|
||||
CC_F += -fno-caret-diagnostics
|
||||
else # (arm)linux
|
||||
|
||||
# get gcc version
|
||||
ifndef _GCC_VERSION
|
||||
_GCC_VERSION:=$(wordlist 1,2,$(subst ., ,$(shell $(CC) -dumpversion)))
|
||||
export _GCC_VERSION
|
||||
endif
|
||||
GCC_VERSION=$(firstword $(_GCC_VERSION)).$(lastword $(_GCC_VERSION))
|
||||
|
||||
CC_WNO += -Wno-unused-local-typedefs
|
||||
CC_F += -fno-diagnostics-show-caret
|
||||
|
||||
CC_DEFS-$(call gte,$(GCC_VERSION),5.0) += _GLIBCXX_USE_CXX11_ABI=0
|
||||
CC_W-$(call gte,$(GCC_VERSION),7.0) += -Wimplicit-fallthrough=0
|
||||
CXX_WNO-$(call gte,$(GCC_VERSION),8.0) += -Wno-class-memaccess
|
||||
|
||||
# suppress warning about ABI change in GCC 4.4
|
||||
CC_WNO-$(__ARMLINUX__) += -Wno-psabi
|
||||
endif
|
||||
|
||||
# optimization cflags
|
||||
ifdef NDEBUG
|
||||
CC_F += -fdata-sections
|
||||
CC_F += -ffunction-sections
|
||||
ifndef __ASAN__
|
||||
CC_F += -fomit-frame-pointer
|
||||
endif
|
||||
# stack protector
|
||||
ifdef __TARGET_MAC_HOST_LINUX__
|
||||
# disable stack protector for our osxcross toolchain (we check
|
||||
# against __TARGET_MAC_HOST_LINUX__ since it is hard to check
|
||||
# for version number in clang).
|
||||
else ifeq ($(call gte,$(GCC_VERSION),4.9),1)
|
||||
CC_F += -fstack-protector-strong
|
||||
else
|
||||
CC_F += -fstack-protector
|
||||
endif
|
||||
CC_DEFS += NDEBUG
|
||||
CC_DEFS += _FORTIFY_SOURCE=2
|
||||
else
|
||||
CC_DEFS += _DEBUG
|
||||
endif
|
||||
|
||||
# system-specific ldflags
|
||||
ifdef __LINUX__
|
||||
LDFLAGS += -Wl,--build-id
|
||||
LDFLAGS += -Wl,--gc-sections
|
||||
LDFLAGS += -Wl,--warn-shared-textrel
|
||||
|
||||
NO_UNDEFS = -Wl,--no-undefined
|
||||
DLL_W += $(NO_UNDEFS)
|
||||
else ifdef __MAC__
|
||||
LDFLAGS += -Wl,-dead_strip
|
||||
|
||||
ifndef __TARGET_MAC_HOST_LINUX__
|
||||
DLL_X += -compatibility_version 1.0
|
||||
DLL_X += -current_version 1.0
|
||||
DLL_X += -single_module
|
||||
endif
|
||||
endif
|
||||
|
||||
# common linker/compiler flags
|
||||
ifdef NDEBUG
|
||||
CCOPT += -O2
|
||||
ifdef __LINUX__
|
||||
LDOPT += -Wl,-O1
|
||||
endif
|
||||
endif
|
||||
|
||||
# AddressSanitizer flags
|
||||
ifdef __ASAN__
|
||||
CC_DEFS += __ASAN__
|
||||
CC_F += -fno-omit-frame-pointer
|
||||
CC_F += -fsanitize=address
|
||||
LDFLAGS += -fsanitize=address
|
||||
export LSAN_OPTIONS=suppressions=$(IDA)etc/bin/known_leaks.txt:detect_leaks=0
|
||||
endif
|
||||
|
||||
# final compiler flags
|
||||
CC_F += $(CC_F-1)
|
||||
CC_W += $(CC_W-1)
|
||||
CC_WNO += $(CC_WNO-1)
|
||||
CXX_WNO += $(CXX_WNO-1)
|
||||
CC_DEFS += $(CC_DEFS-1)
|
||||
CC_INCP += $(CC_INCP-1)
|
||||
CC_D += $(addprefix -D,$(CC_DEFS))
|
||||
CC_I += $(addprefix -I,$(CC_INCP))
|
||||
|
||||
# the -Wno-* flags must come after the -W enabling flags
|
||||
WARNS = $(sort $(CC_W)) $(sort $(CC_WNO))
|
||||
|
||||
CFLAGS += $(sort $(CC_X))
|
||||
CFLAGS += $(CCOPT)
|
||||
CFLAGS += $(sort $(CC_I))
|
||||
CFLAGS += $(sort $(CC_D))
|
||||
CFLAGS += $(sort $(CC_F))
|
||||
CFLAGS += $(WARNS)
|
||||
CFLAGS += $(PTHR_SWITCH)
|
||||
|
||||
# for warning suppression, override the WARNS variable with NOWARNS:
|
||||
# $(TARGET): WARNS = $(NOWARNS)
|
||||
NOWARNS = -w
|
||||
|
||||
# dll linker flags
|
||||
DLLFLAGS += $(DLL_W) $(DLL_X)
|
||||
|
||||
else ifeq ($(COMPILER_NAME),vc)
|
||||
# for warning suppression, override the WARNS variable with NOWARNS:
|
||||
# $(TARGET): WARNS = $(NOWARNS)
|
||||
NOWARNS = -w -wd4702 -wd4738
|
||||
|
||||
# optimization ldflags
|
||||
LDOPT += /DEBUG
|
||||
ifdef NDEBUG
|
||||
LDOPT += /INCREMENTAL:NO /OPT:ICF /OPT:REF
|
||||
endif
|
||||
|
||||
# set c runtime to use
|
||||
ifdef NDEBUG
|
||||
ifdef USE_STATIC_RUNTIME
|
||||
RUNTIME_LIBSW = /MT
|
||||
else
|
||||
RUNTIME_LIBSW = /MD
|
||||
endif
|
||||
else
|
||||
ifdef USE_STATIC_RUNTIME
|
||||
RUNTIME_LIBSW = /MTd
|
||||
else
|
||||
RUNTIME_LIBSW = /MDd
|
||||
endif
|
||||
endif
|
||||
|
||||
# PDB options
|
||||
PDBFLAGS = /PDB:$(PDBDIR)/
|
||||
ifdef NDEBUG
|
||||
PDBFLAGS += /PDBALTPATH:%_PDB%
|
||||
endif
|
||||
# Generate debug info (old style)
|
||||
PDBFORMAT = /Z7
|
||||
|
||||
# final compiler flags
|
||||
CC_DEFS += $(DEF64)
|
||||
CC_DEFS += $(DEFX86)
|
||||
CC_DEFS += $(CC_DEFS-1)
|
||||
CC_INCP += $(CC_INCP-1)
|
||||
CC_D += $(addprefix -D,$(CC_DEFS))
|
||||
CC_I += $(addprefix -I,$(CC_INCP))
|
||||
|
||||
CFGFILE = @$(IDA)$(SYSDIR).cfg
|
||||
CFLAGS += $(CFGFILE)
|
||||
CFLAGS += $(RUNTIME_LIBSW)
|
||||
CFLAGS += $(PDBFORMAT)
|
||||
CFLAGS += /Brepro
|
||||
CFLAGS += $(sort $(CC_I))
|
||||
CFLAGS += $(sort $(CC_D))
|
||||
CFLAGS += $(sort $(CC_F))
|
||||
CFLAGS += $(WARNS)
|
||||
|
||||
# final linker flags
|
||||
LDFLAGS += /Brepro
|
||||
LDFLAGS += $(PDBFLAGS)
|
||||
LDFLAGS += /ERRORREPORT:QUEUE
|
||||
ifdef __X86__
|
||||
LDFLAGS += /LARGEADDRESSAWARE
|
||||
endif
|
||||
|
||||
ifdef __XPCOMPAT__
|
||||
XPSUBSYS-$(__X64__) = /SUBSYSTEM:CONSOLE,5.02
|
||||
XPSUBSYS-$(__X86__) = /SUBSYSTEM:CONSOLE,5.01
|
||||
LDFLAGS += $(XPSUBSYS-1)
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
# to enable obsolete functions, disable the NO_OBSOLETE_FUNCS variable:
|
||||
# $(TARGET): NO_OBSOLETE_FUNCS =
|
||||
NO_OBSOLETE_FUNCS = NO_OBSOLETE_FUNCS
|
||||
CC_DEFS += $(NO_OBSOLETE_FUNCS)
|
||||
|
||||
CXXFLAGS += $(CXXSTD)
|
||||
CXXFLAGS += $(CFLAGS)
|
||||
CXXFLAGS += $(sort $(CXX_F))
|
||||
CXXFLAGS += $(sort $(CXX_WNO))
|
||||
|
||||
LDFLAGS += $(LDOPT)
|
||||
|
||||
#############################################################################
|
||||
ifdef __X86__
|
||||
DEFX86 = __X86__
|
||||
endif
|
||||
|
||||
ifdef __EA64__
|
||||
SUFF64=64
|
||||
ADRSIZE=64
|
||||
DEF64 = __EA64__
|
||||
else
|
||||
ADRSIZE=32
|
||||
endif
|
||||
|
||||
ifdef NDEBUG
|
||||
OPTSUF=_opt
|
||||
endif
|
||||
|
||||
ifdef IDAHOME
|
||||
EXTRASUF=_home
|
||||
IDAHOME_PROCESSORS=pc arm ppc mips mc68k
|
||||
else
|
||||
ifdef USE_STATIC_RUNTIME
|
||||
EXTRASUF=_s
|
||||
endif
|
||||
endif
|
||||
|
||||
#############################################################################
|
||||
SYSDIR=$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(OPTSUF)$(EXTRASUF)
|
||||
# libraries directory
|
||||
LIBDIR=$(IDA)lib/$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(EXTRASUF)
|
||||
# object files directory (using ?= to allow overriding)
|
||||
OBJDIR?=obj/$(SYSDIR)
|
||||
# PDB files directory
|
||||
PDBDIR=$(IDA)pdb/$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(EXTRASUF)
|
||||
# output directory for target platform
|
||||
R=$(IDA)bin/
|
||||
# input directory with existing build utilities
|
||||
RS=$(IDA)bin/
|
||||
# _ida.hlp placed in main tool directory
|
||||
HI=$(RS)
|
||||
# help source
|
||||
HS=.hls
|
||||
# help headers
|
||||
HH=.hhp
|
||||
# include,help and other directories are common for all platforms and compilers:
|
||||
I =$(IDA)include/
|
||||
C =$(R)cfg/
|
||||
RI=$(R)idc/
|
||||
F=$(OBJDIR)/
|
||||
L=$(LIBDIR)/
|
||||
|
||||
DUMB=$(L)dumb$(O)
|
||||
HELP=$(L)help$(O)
|
||||
HLIB=$(HI)_ida.hlp
|
||||
|
||||
# to be used like this:
|
||||
# $(L)va$(A): $(call lib, $(VA_OBJS))
|
||||
lib=$(1); $(strip $(QARf)$(AR) $(OUTAR)$$@ $$^)
|
||||
|
||||
# to be used like this: $(call _link_exe, target, objs, libs)
|
||||
_link_exe=$(strip $(QCCL)$(CCL) $(OUTSW)$(1) $(2) $(3) $(LDFLAGS) $(STDLIBS))
|
||||
|
||||
# to be used like this: $(call link_exe, objs, libs)
|
||||
link_exe=$(call _link_exe,$@,$(1),$(2))
|
||||
|
||||
# to be used like this: $(call _link_dll, target, objs, libs)
|
||||
_link_dll=$(strip $(QCCL)$(CCL) $(OUTDLL) $(DLLFLAGS) $(OUTSW)$(1) $(2) $(3) $(LDFLAGS) $(STDLIBS))
|
||||
|
||||
# to be used like this: $(call link_dll, objs, libs)
|
||||
link_dll=$(call _link_dll,$@,$(1),$(2))
|
||||
|
||||
# to be used like this: $(call link_dumb, target, libs, objs)
|
||||
link_dumb=$(3) $(patsubst %,$(L)%$(A),$(2)); $(strip $(QCCLf)$(CCL) $(OUTSW)$(1) $(LDFLAGS) $(3) $(patsubst %,$(L)%$(A),$(2)) $(STDLIBS))
|
||||
|
||||
# to be used like this:
|
||||
# target: $(call dumb_target, libs, objs) extra_ldflags
|
||||
dumb_target=$(call link_dumb,$$@,$(1),$(2) $(DUMB))
|
||||
|
||||
# to be used like this:
|
||||
# $(R)%$(B): $(F)%$(O) $(call dumb_pattern, libs, objs) extra_ldflags
|
||||
dumb_pattern=$(call link_dumb,$$@ $$<,$(1),$(2) $(DUMB))
|
||||
|
||||
# to be used like this:
|
||||
# OBJS += $(call objs,obj1 obj2 obj3 ...)
|
||||
objs=$(addprefix $(F),$(addsuffix $(O),$(1)))
|
||||
|
||||
# output name for module dll
|
||||
module_dll=$(BIN_PATH)$(1)$(SUFF64)$(DLLEXT)
|
||||
|
||||
# output name for server executable
|
||||
server_exe=$(R)dbgsrv/$(1)
|
||||
|
||||
ifeq ($(or $(M),$(MM),$(MMH),$(MO),$(MMO),$(MMHO)),1)
|
||||
BUILD_IDA = 1
|
||||
endif
|
||||
ifeq ($(or $(M32),$(MM),$(MO32),$(MMO)),1)
|
||||
BUILD_DBGSRV = 1
|
||||
endif
|
||||
|
||||
# target-os specific variables
|
||||
ifdef __NT__
|
||||
DLLEXT=.dll
|
||||
else ifdef __MAC__
|
||||
DLLEXT=.dylib
|
||||
else
|
||||
DLLEXT=.so
|
||||
endif
|
||||
|
||||
# build system commands
|
||||
ifeq ($(OS),Windows_NT)
|
||||
CP=cp -f --preserve=all
|
||||
MKDIR=-@mkdir
|
||||
AWK=gawk
|
||||
else
|
||||
CP=cp -f
|
||||
MKDIR=-@mkdir 2>/dev/null
|
||||
AWK=awk
|
||||
endif
|
||||
RM=rm -f
|
||||
MV=mv
|
||||
|
||||
# used to silence some makefile commands
|
||||
# run 'make Q=' to prevent commands from being silenced
|
||||
Q?=@
|
||||
|
||||
# some makefiles rebuild targets when the makefile itself changes.
|
||||
# this makes debugging makefiles a pain.
|
||||
# run 'make MAKEFILE_DEP=' to disable this behaviour.
|
||||
MAKEFILE_DEP?=makefile
|
||||
|
||||
# libida-related
|
||||
# Note: $(IDALIB) should be used in the dependency list
|
||||
# $(LINKIDA) should be used in the link command
|
||||
ifdef __NT__
|
||||
# Note: on Windows, ida.lib does not have a "64" suffix for ea64
|
||||
IDALIB = $(L)ida$(A)
|
||||
LINKIDA = $(IDALIB)
|
||||
else
|
||||
IDALIB = $(L)libida$(SUFF64)$(DLLEXT)
|
||||
LINKIDA = -L$(L) -lida$(SUFF64)
|
||||
endif
|
||||
|
||||
# simplify command echo
|
||||
ifdef IDAMAKE_SIMPLIFY
|
||||
ifeq ($(Q),@)
|
||||
DO_IDAMAKE_SIMPLIFY=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef DO_IDAMAKE_SIMPLIFY
|
||||
ifdef IDAMAKE_SIMPLIFY_NO_COLOR
|
||||
qcolor=$(1)
|
||||
else
|
||||
ifeq ($(OS),Windows_NT)
|
||||
qcolor=-e #
|
||||
endif
|
||||
qcolor+="\033[1;34m$(1)\033[0m"
|
||||
endif
|
||||
QCXX = @echo $(call qcolor,compile) $< && #
|
||||
QCC = @echo $(call qcolor,compile) $< && #
|
||||
QASM = @echo $(call qcolor,asm) $< && #
|
||||
QARf = @echo $(call qcolor,lib) $$@ && #
|
||||
QCCL = @echo $(call qcolor,link) $@ && #
|
||||
QCCLf = @echo $(call qcolor,link) $$@ && #
|
||||
endif
|
||||
|
||||
# simple build rules
|
||||
CONLY?=-c
|
||||
|
||||
$(F)%$(O): %.cpp
|
||||
$(strip $(QCXX)$(CXX) $(CXXFLAGS) $(NORTTI) $(CONLY) $(OBJSW)$@ $<)
|
||||
|
||||
$(F)%$(O): %.c
|
||||
$(strip $(QCC)$(CC) $(CFLAGS) $(CONLY) $(OBJSW)$@ $(FORCEC) $<)
|
||||
|
||||
$(C)%.cfg: %.cfg
|
||||
$(CP) $? $@
|
||||
|
||||
# http://www.cmcrossroads.com/article/printing-value-makefile-variable
|
||||
print-%:
|
||||
@echo $* = "$($*)"
|
||||
@echo $*\'s origin is $(origin $*)
|
||||
|
||||
#############################################################################
|
||||
.PHONY: all test cfg includes
|
||||
|
||||
# Force make to delete the target if the rule to build it fails
|
||||
.DELETE_ON_ERROR:
|
||||
@@ -1,294 +0,0 @@
|
||||
#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
|
||||
|
||||
|
||||
#include "arm_regs.hpp"
|
||||
#include "deb_arm.hpp"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
arm_debmod_t::arm_debmod_t()
|
||||
{
|
||||
static const uchar bpt[] = ARM_BPT_CODE;
|
||||
bpt_code.append(bpt, sizeof(bpt));
|
||||
sp_idx = R_SP;
|
||||
pc_idx = R_PC;
|
||||
lr_idx = R_LR;
|
||||
sr_idx = R_PSR;
|
||||
nregs = qnumber(arm_registers);
|
||||
|
||||
is_xscale = false;
|
||||
for ( size_t i = 0; i < 2; i++ )
|
||||
{
|
||||
databpts[i] = BADADDR;
|
||||
codebpts[i] = BADADDR;
|
||||
dbptypes[i] = -1;
|
||||
cbptypes[i] = -1;
|
||||
}
|
||||
dbcon = 0;
|
||||
set_platform("linux");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi arm_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t /*ea*/, int len)
|
||||
{
|
||||
if ( type == BPT_SOFT )
|
||||
return BPT_OK;
|
||||
|
||||
if ( !is_xscale )
|
||||
return BPT_BAD_TYPE; // hardware bpts are supported only for xScale
|
||||
|
||||
// For some reason hardware instruction breakpoints do not work
|
||||
if ( type == BPT_EXEC )
|
||||
return BPT_BAD_TYPE;
|
||||
|
||||
if ( len > 4 )
|
||||
return BPT_BAD_LEN;
|
||||
|
||||
bool ok = databpts[0] == BADADDR || databpts[1] == BADADDR;
|
||||
|
||||
return ok ? BPT_OK : BPT_TOO_MANY;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool arm_debmod_t::add_hwbpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
// msg("add_hwbpt %d %a %d\n", type, ea, len);
|
||||
if ( !is_xscale || len > 4 )
|
||||
return false;
|
||||
|
||||
if ( !init_hwbpt_support() )
|
||||
return false;
|
||||
|
||||
if ( type == BPT_EXEC )
|
||||
{
|
||||
if ( codebpts[0] != BADADDR && codebpts[1] != BADADDR )
|
||||
return false;
|
||||
|
||||
int slot = codebpts[0] != BADADDR;
|
||||
codebpts[slot] = ea;
|
||||
cbptypes[slot] = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( databpts[0] != BADADDR && databpts[1] != BADADDR )
|
||||
return false;
|
||||
|
||||
int slot = databpts[0] != BADADDR;
|
||||
int bits;
|
||||
switch ( type )
|
||||
{
|
||||
case BPT_WRITE:
|
||||
bits = 1; // store only
|
||||
break;
|
||||
case BPT_RDWR:
|
||||
bits = 2; // load/store
|
||||
break;
|
||||
// BPT_READ: // load only
|
||||
// bits = 3;
|
||||
// break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
databpts[slot] = ea;
|
||||
dbptypes[slot] = type;
|
||||
dbcon |= bits << (slot*2);
|
||||
}
|
||||
return enable_hwbpts();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool arm_debmod_t::del_hwbpt(ea_t ea, bpttype_t type)
|
||||
{
|
||||
// msg("del_hwbpt %a\n", ea);
|
||||
if ( databpts[0] == ea && dbptypes[0] == type )
|
||||
{
|
||||
databpts[0] = BADADDR;
|
||||
dbcon &= ~3;
|
||||
}
|
||||
else if ( databpts[1] == ea && dbptypes[1] == type )
|
||||
{
|
||||
databpts[1] = BADADDR;
|
||||
dbcon &= ~(3<<2);
|
||||
}
|
||||
else if ( codebpts[0] == ea && cbptypes[0] == type )
|
||||
{
|
||||
codebpts[0] = BADADDR;
|
||||
}
|
||||
else if ( codebpts[1] == ea && cbptypes[1] == type )
|
||||
{
|
||||
codebpts[1] = BADADDR;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return enable_hwbpts();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void arm_debmod_t::cleanup_hwbpts()
|
||||
{
|
||||
databpts[0] = BADADDR;
|
||||
databpts[1] = BADADDR;
|
||||
codebpts[0] = BADADDR;
|
||||
codebpts[1] = BADADDR;
|
||||
dbcon = 0;
|
||||
// disable all bpts
|
||||
if ( is_xscale )
|
||||
disable_hwbpts();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int arm_debmod_t::finalize_appcall_stack(
|
||||
call_context_t &ctx,
|
||||
regval_map_t ®s,
|
||||
bytevec_t &/*stk*/)
|
||||
{
|
||||
regs[lr_idx].ival = ctx.ctrl_ea;
|
||||
// return addrsize as the adjustment factor to add to sp
|
||||
// we do not need the return address, that's why we ignore the first 4
|
||||
// bytes of the prepared stack image
|
||||
return debapp_attrs.addrsize;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int arm_debmod_t::get_regidx(const char *regname, int *clsmask)
|
||||
{
|
||||
return arm_get_regidx(clsmask, regname);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_LOWCNDS
|
||||
//--------------------------------------------------------------------------
|
||||
static const regval_t &idaapi arm_getreg(const char *name, const regval_t *regvals)
|
||||
{
|
||||
int idx = ssmod->get_regidx(name, NULL);
|
||||
QASSERT(30182, idx >= 0 && idx < ssmod->nregs);
|
||||
return regvals[idx];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static uint32 idaapi arm_get_long(ea_t ea)
|
||||
{
|
||||
uint32 v = -1;
|
||||
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
|
||||
return v;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static uint16 idaapi arm_get_word(ea_t ea)
|
||||
{
|
||||
uint16 v = -1;
|
||||
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
|
||||
return v;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static uint8 idaapi arm_get_byte(ea_t ea)
|
||||
{
|
||||
uint8 v = -1;
|
||||
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
|
||||
return v;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// stripped down version of get_dtype_size()
|
||||
static size_t idaapi arm_get_dtype_size(op_dtype_t dtype)
|
||||
{
|
||||
switch ( dtype )
|
||||
{
|
||||
case dt_byte: return 1; // 8 bit
|
||||
case dt_word:
|
||||
case dt_half: return 2; // 16 bit
|
||||
case dt_dword:
|
||||
case dt_float: return 4; // 4 byte
|
||||
case dt_qword:
|
||||
case dt_double: return 8; // 8 byte
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// since arm does not have a single step facility, we have to emulate it
|
||||
// with a temporary breakpoint.
|
||||
drc_t arm_debmod_t::dbg_perform_single_step(debug_event_t *dev, const insn_t &insn)
|
||||
{
|
||||
// read register values
|
||||
regvals_t values;
|
||||
values.resize(nregs);
|
||||
drc_t drc = dbg_read_registers(dev->tid, ARM_RC_GENERAL, values.begin(), NULL);
|
||||
if ( drc <= DRC_NONE )
|
||||
return drc;
|
||||
|
||||
static const opinfo_helpers_t oh =
|
||||
{
|
||||
arm_getreg,
|
||||
arm_get_byte,
|
||||
arm_get_word,
|
||||
arm_get_long,
|
||||
arm_get_dtype_size,
|
||||
NULL, // has_insn_cf_chg not needed
|
||||
};
|
||||
|
||||
// calculate the address of the next executed instruction
|
||||
lock_begin();
|
||||
ssmod = this;
|
||||
ea_t next = calc_next_exec_insn(insn, values.begin(), oh, false); // TODO pass is_mprofile parameter
|
||||
ssmod = NULL;
|
||||
lock_end();
|
||||
|
||||
// BADADDR means that the execution flow is linear
|
||||
if ( next == BADADDR )
|
||||
{
|
||||
next = insn.ea + insn.size;
|
||||
if ( (values[sr_idx].ival & BIT5) != 0 ) // thumb?
|
||||
next |= 1;
|
||||
}
|
||||
|
||||
// safety check: self jumping instruction cannot be single stepped
|
||||
if ( (next & ~1) == insn.ea )
|
||||
return DRC_FAILED;
|
||||
|
||||
// add a breakpoint there
|
||||
update_bpt_info_t ubi;
|
||||
ubi.ea = next;
|
||||
ubi.type = BPT_SOFT;
|
||||
ubi.code = 0;
|
||||
int nbpts;
|
||||
drc = dbg_update_bpts(&nbpts, &ubi, 1, 0, NULL);
|
||||
if ( drc != DRC_OK || nbpts == 0 )
|
||||
return drc != DRC_OK ? drc : DRC_FAILED;
|
||||
|
||||
drc = resume_app_and_get_event(dev);
|
||||
|
||||
// clean up: delete the temporary breakpoint
|
||||
ubi.ea &= ~1; // del_bpt requires an even address
|
||||
drc_t drc2 = dbg_update_bpts(&nbpts, &ubi, 0, 1, NULL);
|
||||
if ( drc2 != DRC_OK || nbpts == 0 )
|
||||
{
|
||||
msg("%a: failed to remove single step bpt?!\n", ubi.ea);
|
||||
drc = drc2 != DRC_OK ? drc2 : DRC_FAILED;
|
||||
}
|
||||
// the caller expects to see STEP after us:
|
||||
if ( drc == DRC_OK )
|
||||
dev->set_eid(STEP);
|
||||
return drc;
|
||||
}
|
||||
|
||||
#endif // ENABLE_LOWCNDS
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void arm_debmod_t::adjust_swbpt(ea_t *p_ea, int *p_len)
|
||||
{
|
||||
ea_t &ea = *p_ea;
|
||||
if ( (ea & 1) != 0 ) // T bit is set, use a thumb breakpoint
|
||||
{
|
||||
ea--;
|
||||
*p_len = 2;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#ifndef __ARM_DEBMOD__
|
||||
#define __ARM_DEBMOD__
|
||||
|
||||
#include "deb_arm.hpp"
|
||||
#include "debmod.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class arm_debmod_t : public debmod_t
|
||||
{
|
||||
typedef debmod_t inherited;
|
||||
protected:
|
||||
bool is_xscale;
|
||||
ea_t databpts[2];
|
||||
ea_t codebpts[2];
|
||||
bpttype_t dbptypes[2];
|
||||
bpttype_t cbptypes[2];
|
||||
int dbcon;
|
||||
|
||||
int lr_idx;
|
||||
int sr_idx;
|
||||
|
||||
public:
|
||||
arm_debmod_t();
|
||||
void cleanup_hwbpts();
|
||||
|
||||
bool del_hwbpt(ea_t ea, bpttype_t type);
|
||||
bool add_hwbpt(bpttype_t type, ea_t ea, int len);
|
||||
ea_t is_hwbpt_triggered(thid_t id, bool is_stepping);
|
||||
|
||||
inline bool active_databpts(void)
|
||||
{
|
||||
return databpts[0] != BADADDR || databpts[1] != BADADDR;
|
||||
}
|
||||
|
||||
inline bool active_codebpts(void)
|
||||
{
|
||||
return codebpts[0] != BADADDR || codebpts[1] != BADADDR;
|
||||
}
|
||||
|
||||
inline bool active_hwbpts(void)
|
||||
{
|
||||
return active_databpts() || active_codebpts();
|
||||
}
|
||||
|
||||
// overridden base class functions
|
||||
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) override;
|
||||
virtual int finalize_appcall_stack(call_context_t &ctx, regval_map_t ®s, bytevec_t &stk) override;
|
||||
|
||||
// new virtial functions
|
||||
virtual bool init_hwbpt_support() newapi { return true; }
|
||||
virtual bool disable_hwbpts() newapi { return false; }
|
||||
virtual bool enable_hwbpts() newapi { return false; }
|
||||
virtual bool refresh_hwbpts() newapi { return false; }
|
||||
|
||||
virtual int get_regidx(const char *regname, int *clsmask) override;
|
||||
virtual void adjust_swbpt(ea_t *p_ea, int *p_len) override;
|
||||
|
||||
protected:
|
||||
#ifdef ENABLE_LOWCNDS
|
||||
virtual drc_t dbg_perform_single_step(debug_event_t *dev, const insn_t &insn) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
bool is_32bit_thumb_insn(uint16 code);
|
||||
|
||||
#endif
|
||||
@@ -1,107 +0,0 @@
|
||||
#include <set>
|
||||
|
||||
#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)
|
||||
{
|
||||
if ( type == BPT_SOFT )
|
||||
{
|
||||
if ( (ea & 1) != 0 )
|
||||
return BPT_BAD_ADDR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( type != BPT_RDWR // type is good?
|
||||
&& type != BPT_WRITE
|
||||
&& type != BPT_EXEC )
|
||||
{
|
||||
return BPT_BAD_TYPE;
|
||||
}
|
||||
|
||||
if ( (ea & (len-1)) != 0 ) // alignment is good?
|
||||
return BPT_BAD_ALIGN;
|
||||
|
||||
if ( len != 1 )
|
||||
{
|
||||
warning("AUTOHIDE REGISTRY\n"
|
||||
"xScale supports only 1 byte length hardware breakpoints");
|
||||
return BPT_BAD_LEN;
|
||||
}
|
||||
}
|
||||
return BPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// if bit0 is set, ensure that thumb mode
|
||||
// if bit0 is clear, ensure that arm mode
|
||||
static void handle_arm_thumb_modes(ea_t ea)
|
||||
{
|
||||
bool should_be_thumb = (ea & 1) != 0;
|
||||
bool is_thumb = processor_t::get_code16_mode(ea);
|
||||
if ( should_be_thumb != is_thumb )
|
||||
processor_t::set_code16_mode(ea, should_be_thumb);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static easet_t pending_addresses;
|
||||
|
||||
static ssize_t idaapi dbg_callback(void *, int code, va_list)
|
||||
{
|
||||
// we apply thumb/arm switches when the process is suspended.
|
||||
// it is quite late (normally we should do it as soon as the corresponding
|
||||
// segment is created) but i did not manage to make it work.
|
||||
// in the segm_added event the addresses are not enabled yet,
|
||||
// so switching modes fails.
|
||||
if ( code == dbg_suspend_process && !pending_addresses.empty() )
|
||||
{
|
||||
for ( easet_t::iterator p=pending_addresses.begin();
|
||||
p != pending_addresses.end();
|
||||
++p )
|
||||
{
|
||||
handle_arm_thumb_modes(*p);
|
||||
}
|
||||
pending_addresses.clear();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// For ARM processors the low bit means 1-thumb, 0-arm mode.
|
||||
// The following function goes over the address list and sets the mode
|
||||
// in IDA database according to bit0. It also resets bit0 for all addresses.
|
||||
void set_arm_thumb_modes(ea_t *addrs, int qty)
|
||||
{
|
||||
for ( int i=0; i < qty; i++ )
|
||||
{
|
||||
ea_t ea = addrs[i];
|
||||
segment_t *s = getseg(ea);
|
||||
if ( s == NULL )
|
||||
pending_addresses.insert(ea);
|
||||
else
|
||||
handle_arm_thumb_modes(ea);
|
||||
|
||||
addrs[i] = ea & ~1;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void processor_specific_init(void)
|
||||
{
|
||||
hook_to_notification_point(HT_DBG, dbg_callback);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void processor_specific_term(void)
|
||||
{
|
||||
unhook_from_notification_point(HT_DBG, dbg_callback);
|
||||
pending_addresses.clear();
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
|
||||
#include "arm_regs.hpp"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with register_class_arm_t
|
||||
const char *arm_register_classes[] =
|
||||
{
|
||||
"General registers",
|
||||
"VFP registers",
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifndef __EA64__
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const psr[] =
|
||||
{
|
||||
"MODE", // 0
|
||||
"MODE", // 1
|
||||
"MODE", // 2
|
||||
"MODE", // 3
|
||||
"MODE", // 4
|
||||
"T", // 5
|
||||
"F", // 6
|
||||
"I", // 7
|
||||
"A", // 8
|
||||
"E", // 9
|
||||
"IT", // 10
|
||||
"IT", // 11
|
||||
"IT", // 12
|
||||
"IT", // 13
|
||||
"IT", // 14
|
||||
"IT", // 15
|
||||
"GE", // 16
|
||||
"GE", // 17
|
||||
"GE", // 18
|
||||
"GE", // 19
|
||||
NULL, // 20
|
||||
NULL, // 21
|
||||
NULL, // 22
|
||||
NULL, // 23
|
||||
"J", // 24
|
||||
"IT2", // 25 additional bits of IT
|
||||
"IT2", // 26 additional bits of IT
|
||||
"Q", // 27
|
||||
"V", // 28
|
||||
"C", // 29
|
||||
"Z", // 30
|
||||
"N", // 31
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const vfp_format[] =
|
||||
{
|
||||
"VFP_1_double",
|
||||
};
|
||||
#else
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const psr[] =
|
||||
{
|
||||
"M", // 0 AArch32 mode that an exception was taken from
|
||||
"M", // 1
|
||||
"M", // 2
|
||||
"M", // 3
|
||||
"M", // 4 Execution state that the exception was taken from
|
||||
"T", // 5 T32 Instruction set state bit
|
||||
"F", // 6 FIQ mask bit
|
||||
"I", // 7 IRQ mask bit
|
||||
"A", // 8 Asynchronous data abort mask bit
|
||||
"E", // 9 Endianness Execution State bit
|
||||
"IT", // 10 If-Then
|
||||
"IT", // 11
|
||||
"IT", // 12
|
||||
"IT", // 13
|
||||
"IT", // 14
|
||||
"IT", // 15
|
||||
"GE", // 16 Greater than or Equal flags
|
||||
"GE", // 17
|
||||
"GE", // 18
|
||||
"GE", // 19
|
||||
"IL", // 20 Illegal Execution State bit
|
||||
NULL, // 21
|
||||
NULL, // 22
|
||||
NULL, // 23
|
||||
NULL, // 24
|
||||
"IT2", // 25 If-Then
|
||||
"IT2", // 26
|
||||
"Q", // 27 Cumulative saturation bit
|
||||
"V", // 28 oVerflow condition flag
|
||||
"C", // 29 Carry condition flag
|
||||
"Z", // 30 Zero condition flag
|
||||
"N", // 31 Negative condition flag
|
||||
};
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with register_arm_t
|
||||
register_info_t arm_registers[] =
|
||||
{
|
||||
#ifndef __EA64__
|
||||
// General registers
|
||||
{ "R0", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R1", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R2", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R3", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R4", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R5", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R6", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R7", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R8", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R9", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R10", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R11", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R12", REGISTER_ADDRESS|REGISTER_FP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "SP", REGISTER_ADDRESS|REGISTER_SP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "LR", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "PC", REGISTER_ADDRESS|REGISTER_IP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "PSR", 0, ARM_RC_GENERAL, dt_dword, psr, 0xF800007F },
|
||||
// VFP registers
|
||||
{ "D0", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D1", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D2", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D3", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D4", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D5", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D6", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D7", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D8", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D9", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D10", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D11", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D12", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D13", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D14", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D15", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D16", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D17", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D18", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D19", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D20", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D21", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D22", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D23", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D24", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D25", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D26", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D27", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D28", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D29", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D30", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D31", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "FPSCR", 0, ARM_RC_VFP, dt_dword, NULL, 0 },
|
||||
#else
|
||||
// General registers
|
||||
{ "X0", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X1", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X2", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X3", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X4", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X5", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X6", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X7", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X8", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X9", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X10", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X11", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X12", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X13", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X14", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X15", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X16", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X17", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X18", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X19", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X20", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X21", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X22", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X23", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X24", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X25", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X26", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X27", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X28", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X29", REGISTER_ADDRESS|REGISTER_FP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X30", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "SP", REGISTER_ADDRESS|REGISTER_SP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "PC", REGISTER_ADDRESS|REGISTER_IP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "PSR", 0, ARM_RC_GENERAL, dt_dword, psr, 0xF8000000 },
|
||||
#endif
|
||||
};
|
||||
CASSERT(qnumber(arm_registers) == ARM_NREGS);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int arm_get_regidx(int *clsmask, const char *regname)
|
||||
{
|
||||
for ( size_t i = 0; i < qnumber(arm_registers); i++ )
|
||||
{
|
||||
if ( strieq(regname, arm_registers[i].name) )
|
||||
{
|
||||
if ( clsmask != NULL )
|
||||
*clsmask = arm_registers[i].register_class;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int arm_get_regclass(int idx)
|
||||
{
|
||||
if ( idx >= 0 && idx < qnumber(arm_registers) )
|
||||
return arm_registers[idx].register_class;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pro.h>
|
||||
#include <idd.hpp>
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#if defined(__LINUX__) && defined(__ARM__) && !defined(__EA64__)
|
||||
# define __HAVE_ARM_VFP__
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with arm_register_classes
|
||||
enum register_class_arm_t
|
||||
{
|
||||
ARM_RC_GENERAL = 0x01, // General registers
|
||||
ARM_RC_VFP = 0x02, // VFP registers
|
||||
ARM_RC_ALL = ARM_RC_GENERAL
|
||||
#ifdef __HAVE_ARM_VFP__
|
||||
| ARM_RC_VFP
|
||||
#endif
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with arm_registers
|
||||
enum register_arm_t
|
||||
{
|
||||
#ifndef __EA64__
|
||||
// General registers
|
||||
R_R0,
|
||||
R_R1,
|
||||
R_R2,
|
||||
R_R3,
|
||||
R_R4,
|
||||
R_R5,
|
||||
R_R6,
|
||||
R_R7,
|
||||
R_R8,
|
||||
R_R9,
|
||||
R_R10,
|
||||
R_R11,
|
||||
R_R12,
|
||||
R_SP,
|
||||
R_LR,
|
||||
R_PC,
|
||||
R_PSR,
|
||||
// VFP registers
|
||||
R_D0,
|
||||
R_D1,
|
||||
R_D2,
|
||||
R_D3,
|
||||
R_D4,
|
||||
R_D5,
|
||||
R_D6,
|
||||
R_D7,
|
||||
R_D8,
|
||||
R_D9,
|
||||
R_D10,
|
||||
R_D11,
|
||||
R_D12,
|
||||
R_D13,
|
||||
R_D14,
|
||||
R_D15,
|
||||
R_D16,
|
||||
R_D17,
|
||||
R_D18,
|
||||
R_D19,
|
||||
R_D20,
|
||||
R_D21,
|
||||
R_D22,
|
||||
R_D23,
|
||||
R_D24,
|
||||
R_D25,
|
||||
R_D26,
|
||||
R_D27,
|
||||
R_D28,
|
||||
R_D29,
|
||||
R_D30,
|
||||
R_D31,
|
||||
R_FPSCR,
|
||||
#else
|
||||
// General registers
|
||||
R_R0,
|
||||
R_R1,
|
||||
R_R2,
|
||||
R_R3,
|
||||
R_R4,
|
||||
R_R5,
|
||||
R_R6,
|
||||
R_R7,
|
||||
R_R8,
|
||||
R_R9,
|
||||
R_R10,
|
||||
R_R11,
|
||||
R_R12,
|
||||
R_R13,
|
||||
R_R14,
|
||||
R_R15,
|
||||
R_R16,
|
||||
R_R17,
|
||||
R_R18,
|
||||
R_R19,
|
||||
R_R20,
|
||||
R_R21,
|
||||
R_R22,
|
||||
R_R23,
|
||||
R_R24,
|
||||
R_R25,
|
||||
R_R26,
|
||||
R_R27,
|
||||
R_R28,
|
||||
R_R29,
|
||||
R_LR,
|
||||
R_SP,
|
||||
R_PC,
|
||||
R_PSR,
|
||||
#endif
|
||||
};
|
||||
|
||||
// Number of registers in arm and aarch64
|
||||
#define ARM64_NREGS 34
|
||||
#define ARM32_NREGS 50
|
||||
|
||||
#ifdef __EA64__
|
||||
#define ARM_NREGS ARM64_NREGS
|
||||
#else
|
||||
#define ARM_NREGS ARM32_NREGS
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
extern const char *arm_register_classes[];
|
||||
extern register_info_t arm_registers[ARM_NREGS];
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int arm_get_regidx(int *clsmask, const char *regname);
|
||||
int arm_get_regclass(int idx);
|
||||
@@ -1,906 +0,0 @@
|
||||
#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 execute_sync(tester, MFF_FAST);
|
||||
}
|
||||
|
||||
void send_heartbeat(size_t *page_counter)
|
||||
{
|
||||
*page_counter += 1;
|
||||
if ( *page_counter >= PAGE_HB )
|
||||
{
|
||||
*page_counter = 0;
|
||||
uint32 now = get_secs(qtime64());
|
||||
if ( now - last_hb >= TIME_HB )
|
||||
{
|
||||
mod->dmsg(""); // heartbeat
|
||||
last_hb = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
DECLARE_VIRTUAL_DTOR(matcher_t)
|
||||
{
|
||||
found_ea = NULL;
|
||||
mod = NULL;
|
||||
errbuf = NULL;
|
||||
}
|
||||
|
||||
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) = 0;
|
||||
|
||||
drc_t search_memory_range(ea_t range_start_ea, ea_t range_end_ea)
|
||||
{
|
||||
if ( range_is_unreadable(range_start_ea) )
|
||||
{
|
||||
#ifndef TEST
|
||||
mod->debdeb("dbg_bin_search memory range %a..%a is unreadable, skip it\n", range_start_ea, range_end_ea);
|
||||
#endif
|
||||
return DRC_FAILED;
|
||||
}
|
||||
|
||||
return find(range_start_ea, range_end_ea);
|
||||
}
|
||||
|
||||
virtual drc_t find(ea_t start_ea, ea_t end_ea) = 0;
|
||||
|
||||
bool match_pattern(
|
||||
const uchar *page_ptr,
|
||||
const compiled_binpat_t &ptn,
|
||||
size_t ptn_off,
|
||||
size_t nbytes) const
|
||||
{
|
||||
const uchar *image = ptn.bytes.begin() + ptn_off;
|
||||
const uchar *mask = ptn.all_bytes_defined() ? NULL : ptn.mask.begin() + ptn_off;
|
||||
for ( int i=0; i < nbytes; ++i, ++page_ptr, ++image )
|
||||
{
|
||||
uchar c = *page_ptr;
|
||||
if ( mask != NULL )
|
||||
{
|
||||
if ( mask == SKIP_FF_MASK )
|
||||
{
|
||||
if ( *image == 0xFF )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mask[i] == 0 )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
uchar c2 = *image;
|
||||
if ( sense_case() ? (c != c2) : (qtoupper(c) != qtoupper(c2)) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ea_t get_failed_address(void) const { return failed_ea; }
|
||||
|
||||
private:
|
||||
bool range_is_unreadable(ea_t range_start_ea)
|
||||
{
|
||||
uchar dummy;
|
||||
return mod->dbg_read_memory(range_start_ea, &dummy, sizeof(dummy), NULL) != sizeof(dummy);
|
||||
}
|
||||
};
|
||||
|
||||
typedef janitor_t<matcher_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
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* ALL RIGHTS RESERVED.
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
*
|
||||
*
|
||||
* This file defines the Bochs Debugger module extension functions.
|
||||
* Use debugger_t->get_debmod_extensions() to retrieve this structure.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BOCHSEXT__
|
||||
#define __BOCHSEXT__
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#define BOCHSEXT_VER 1
|
||||
|
||||
struct bochsext_t
|
||||
{
|
||||
// the structure version
|
||||
uint32 version;
|
||||
|
||||
// Sends an arbitrary command to Bochs internal debugger
|
||||
// cmd - command to send
|
||||
// out - pointer to qstring that will hold the output of the command
|
||||
// Returns: true if ok; false if failed to send command to bochs or receive
|
||||
// a reply
|
||||
bool (idaapi *send_command)(const char *cmd, qstring *out);
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* ALL RIGHTS RESERVED.
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
*
|
||||
*
|
||||
* This file defines the functions prototypes that are exported by bochsys.dll
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BOCHSYS_DLL__
|
||||
#define __BOCHSYS_DLL__
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <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
|
||||
@@ -1,857 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
// arm has no single step mechanism
|
||||
// DBG_HAS_SET_RESUME_MODE must be set before init_debugger
|
||||
#if TARGET_PROCESSOR == PLFM_ARM
|
||||
# define S_DBG_HAS_SET_RESUME_MODE 0
|
||||
#else
|
||||
# define S_DBG_HAS_SET_RESUME_MODE DBG_HAS_SET_RESUME_MODE
|
||||
#endif
|
||||
|
||||
#ifndef DEBUGGER_RESMOD
|
||||
# define DEBUGGER_RESMOD 0
|
||||
#endif
|
||||
|
||||
debugger_t debugger =
|
||||
{
|
||||
IDD_INTERFACE_VERSION,
|
||||
DEBUGGER_NAME,
|
||||
DEBUGGER_ID,
|
||||
PROCESSOR_NAME,
|
||||
DEBUGGER_FLAGS, // flags
|
||||
DBG_HAS_ATTACH_PROCESS
|
||||
| DBG_HAS_REQUEST_PAUSE
|
||||
| DBG_HAS_SET_EXCEPTION_INFO
|
||||
| DBG_HAS_THREAD_SUSPEND
|
||||
| DBG_HAS_THREAD_CONTINUE
|
||||
| S_DBG_HAS_SET_RESUME_MODE
|
||||
| DBG_HAS_THREAD_GET_SREG_BASE
|
||||
| DBG_HAS_CHECK_BPT
|
||||
| DBG_HAS_REXEC, // flags2
|
||||
|
||||
REGISTER_CLASSES,
|
||||
REGISTER_CLASSES_DEFAULT,
|
||||
REGISTERS,
|
||||
REGISTERS_SIZE,
|
||||
|
||||
MEMORY_PAGE_SIZE,
|
||||
|
||||
bpt_code,
|
||||
sizeof(bpt_code),
|
||||
S_FILETYPE,
|
||||
DEBUGGER_RESMOD,
|
||||
|
||||
S_SET_DBG_OPTIONS,
|
||||
idd_notify,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// PLUGIN DESCRIPTION BLOCK
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
plugin_t PLUGIN =
|
||||
{
|
||||
IDP_INTERFACE_VERSION,
|
||||
PLUGIN_HIDE|PLUGIN_DBG, // plugin flags
|
||||
init, // initialize
|
||||
|
||||
term, // terminate. this pointer may be NULL.
|
||||
|
||||
run, // invoke plugin
|
||||
|
||||
comment, // long comment about the plugin
|
||||
// it could appear in the status line
|
||||
// or as a hint
|
||||
|
||||
comment, // multiline help about the plugin
|
||||
|
||||
wanted_name, // the preferred short name of the plugin
|
||||
"" // the preferred hotkey to run the plugin
|
||||
};
|
||||
@@ -1,246 +0,0 @@
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
|
||||
#include <network.hpp>
|
||||
|
||||
#include "dbg_rpc_engine.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
dbg_rpc_engine_t::dbg_rpc_engine_t(bool _is_client)
|
||||
: rpc_engine_t(_is_client),
|
||||
has_pending_event(false),
|
||||
poll_debug_events(false)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// sends a request and waits for a reply
|
||||
// may occasionally send another request based on the reply
|
||||
rpc_packet_t *dbg_rpc_engine_t::send_request_and_receive_reply(bytevec_t &req, int flags)
|
||||
{
|
||||
bool displayed = false;
|
||||
rpc_packet_t *result = NULL;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( displayed && user_cancelled() )
|
||||
req = prepare_rpc_packet(RPC_CANCELLED);
|
||||
|
||||
if ( !req.empty() )
|
||||
{
|
||||
int code = send_data(req);
|
||||
if ( code != 0 || (flags & PREQ_GET_EVENT) != 0 )
|
||||
break;
|
||||
|
||||
rpc_packet_t *reqp = (rpc_packet_t *)req.begin();
|
||||
if ( reqp->code == RPC_ERROR )
|
||||
qexit(1); // sent error packet, may die now
|
||||
}
|
||||
|
||||
rpc_packet_t *rp = recv_packet();
|
||||
if ( rp == NULL )
|
||||
break;
|
||||
|
||||
switch ( rp->code )
|
||||
{
|
||||
case RPC_UNK:
|
||||
dwarning("rpc: remote did not understand our request");
|
||||
goto FAILURE;
|
||||
case RPC_MEM:
|
||||
dwarning("rpc: no remote memory");
|
||||
goto FAILURE;
|
||||
case RPC_CANCELLED:
|
||||
msg("rpc: user cancelled the operation\n");
|
||||
goto FAILURE;
|
||||
case RPC_OK:
|
||||
result = rp;
|
||||
goto END;
|
||||
default:
|
||||
// other replies are passed to the handler
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !logged_in )
|
||||
{
|
||||
lprintf("Exploit packet has been detected and ignored\n");
|
||||
FAILURE:
|
||||
qfree(rp);
|
||||
break;
|
||||
}
|
||||
//handle actual command in the request
|
||||
//FIXME: use a better function name
|
||||
req = on_send_request_interrupt(rp);
|
||||
qfree(rp);
|
||||
}
|
||||
|
||||
END:
|
||||
on_send_request_end(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int dbg_rpc_engine_t::_send_request_get_int_result(
|
||||
bytevec_t &req,
|
||||
int failure_code,
|
||||
qstring *errbuf)
|
||||
{
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return failure_code;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int rc = mmdsr.unpack_dd();
|
||||
if ( rc < 0 && errbuf != NULL )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
|
||||
qfree(rp);
|
||||
return rc;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
||||
#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.
|
||||
|
||||
int is_arm_valid_bpt(bpttype_t type, ea_t ea, int len);
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,741 +0,0 @@
|
||||
#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 <map>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <pro.h>
|
||||
#include <idd.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
extern debugger_t debugger;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct name_info_t
|
||||
{
|
||||
eavec_t addrs;
|
||||
qvector<char *> names;
|
||||
void clear(void)
|
||||
{
|
||||
addrs.clear();
|
||||
names.clear();
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Extended process info
|
||||
struct ext_process_info_t : public process_info_t
|
||||
{
|
||||
int addrsize; // process bitness 32bit - 4, 64bit - 8, 0 - unknown
|
||||
qstring ext_name; // human-readable name (e.g. with command line agrs)
|
||||
void copy_to(process_info_t *dst)
|
||||
{
|
||||
dst->pid = pid;
|
||||
dst->name = ext_name.empty() ? name : ext_name;
|
||||
}
|
||||
};
|
||||
typedef qvector<ext_process_info_t> procvec_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Very simple class to store pending events
|
||||
enum queue_pos_t
|
||||
{
|
||||
IN_FRONT,
|
||||
IN_BACK
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct pagebpt_data_t
|
||||
{
|
||||
ea_t ea; // address of the bpt as specified by the user
|
||||
ea_t page_ea; // real address of the bpt as written to the process
|
||||
int user_len; // breakpoint length as specified by the user
|
||||
int aligned_len; // breakpoint length aligned to the page size
|
||||
int real_len; // real length of the breakpoint as written to the process
|
||||
uint32 old_prot; // old page protections (before writing the bpt to the process)
|
||||
// if 0, the bpt has not been written to the process yet.
|
||||
uint32 new_prot; // new page protections (when the bpt is active)
|
||||
bpttype_t type; // breakpoint type
|
||||
};
|
||||
|
||||
// Information about page breakpoints is stored in this data structure.
|
||||
// The map is indexed by the page start address (not the address specified
|
||||
// by the user!)
|
||||
typedef std::map<ea_t, pagebpt_data_t> page_bpts_t; // page_ea -> bpt info
|
||||
typedef qvector<page_bpts_t::iterator> pbpt_iterators_t; // list of iterators into page_bpts_t
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// set of addresses
|
||||
typedef std::set<ea_t> easet_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class idc_value_t;
|
||||
class rpc_engine_t;
|
||||
error_t idaapi idc_get_reg_value(idc_value_t *argv, idc_value_t *r);
|
||||
error_t idaapi idc_set_reg_value(idc_value_t *argv, idc_value_t *r);
|
||||
void report_idc_error(rpc_engine_t *rpc, ea_t ea, error_t code, ssize_t errval, const char *errprm);
|
||||
|
||||
// IDC function name that is exported by a debugger module
|
||||
// to allow scripts to send debugger commands
|
||||
#define IDC_SENDDBG_CMD "send_dbg_command"
|
||||
#define IDC_READ_MSR "read_msr"
|
||||
#define IDC_WRITE_MSR "write_msr"
|
||||
#define IDC_STEP_BACK "step_back"
|
||||
#define IDC_SET_TEV "set_current_tev"
|
||||
#define IDC_GET_TEV "get_current_tev"
|
||||
|
||||
// A macro to convert a pointer to ea_t without sign extension.
|
||||
#define EA_T(ptr) (ea_t)(size_t)(ptr)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-V:debmod_bpt_t:730 not all members of a class are initialized inside the constructor: saved
|
||||
struct debmod_bpt_t
|
||||
{
|
||||
ea_t ea;
|
||||
uchar saved[8]; // size of the biggest supported bpt size (PPC64)
|
||||
uchar nsaved;
|
||||
int bid; // (epoc) breakpoint id (from TRK)
|
||||
debmod_bpt_t() : ea(BADADDR), nsaved(0), bid(0) {}
|
||||
debmod_bpt_t(ea_t _ea, uchar _nsaved) : ea(_ea), nsaved(_nsaved), bid(0) { QASSERT(1796, nsaved < sizeof(saved)); }
|
||||
};
|
||||
typedef std::map<ea_t, debmod_bpt_t> debmodbpt_map_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct eventlist_t : public std::deque<debug_event_t>
|
||||
{
|
||||
public:
|
||||
// save a pending event
|
||||
void enqueue(const debug_event_t &ev, queue_pos_t pos)
|
||||
{
|
||||
if ( pos != IN_BACK )
|
||||
push_front(ev);
|
||||
else
|
||||
push_back(ev);
|
||||
}
|
||||
|
||||
// retrieve a pending event
|
||||
bool retrieve(debug_event_t *event)
|
||||
{
|
||||
if ( empty() )
|
||||
return false;
|
||||
// get the first event and return it
|
||||
*event = front();
|
||||
pop_front();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int send_ioctl(rpc_engine_t *rpc, int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize);
|
||||
int send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty);
|
||||
int send_debug_event_to_ida(const debug_event_t *ev, int rqflags);
|
||||
void set_arm_thumb_modes(ea_t *addrs, int qty);
|
||||
char *debug_event_str(const debug_event_t *ev, char *buf, size_t bufsize);
|
||||
char *debug_event_str(const debug_event_t *ev); // returns static buf
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// describes a dll to be imported
|
||||
struct import_request_t
|
||||
{
|
||||
ea_t base;
|
||||
qstring path;
|
||||
bytevec_t uuid;
|
||||
import_request_t(ea_t _base, const qstring &_path, const bytevec_t &_uuid)
|
||||
: base(_base), path(_path), uuid(_uuid) {}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(import_request_t);
|
||||
typedef qvector<import_request_t> import_infos_t;
|
||||
int import_dll(const import_request_t &req);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct regctx_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct regctx_entry_t
|
||||
{
|
||||
enum type_t
|
||||
{
|
||||
IVAL = 0,
|
||||
FVAL = 1,
|
||||
DATA = 2,
|
||||
FUNC = 3,
|
||||
} type;
|
||||
int reg_class;
|
||||
int reg_idx;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
size_t offset_in_ctx;
|
||||
size_t size_in_ctx;
|
||||
size_t reg_size;
|
||||
};
|
||||
struct
|
||||
{
|
||||
void (*read_func)(const regctx_t *, regval_t *, void *);
|
||||
void (*write_func)(regctx_t *, const regval_t *, void *);
|
||||
void *user_data;
|
||||
};
|
||||
};
|
||||
|
||||
uint64_t truncate_ival(uint64_t ival) const
|
||||
{
|
||||
switch ( reg_size )
|
||||
{
|
||||
case 1: ival = uint8_t(ival); break;
|
||||
case 2: ival = uint16_t(ival); break;
|
||||
case 4: ival = uint32_t(ival); break;
|
||||
}
|
||||
return ival;
|
||||
}
|
||||
|
||||
void read(regval_t *value, const uint8_t *ptr) const
|
||||
{
|
||||
if ( type == regctx_entry_t::FUNC )
|
||||
{
|
||||
read_func((regctx_t *) ptr, value, user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t *p = ptr + offset_in_ctx;
|
||||
switch ( type )
|
||||
{
|
||||
case regctx_entry_t::IVAL:
|
||||
{
|
||||
uint64_t ival = 0;
|
||||
switch ( size_in_ctx )
|
||||
{
|
||||
case 1: ival = *(uint8_t *)p; break;
|
||||
case 2: ival = *(uint16_t *)p; break;
|
||||
case 4: ival = *(uint32_t *)p; break;
|
||||
case 8: ival = *(uint64_t *)p; break;
|
||||
}
|
||||
ival = truncate_ival(ival);
|
||||
value->ival = ival;
|
||||
}
|
||||
break;
|
||||
case regctx_entry_t::FVAL:
|
||||
// TODO: for x86, the value is converted to IEEE format in
|
||||
// x86_read_registers(). clean this up somehow.
|
||||
memcpy(value->fval, p, size_in_ctx);
|
||||
value->rvtype = RVT_FLOAT;
|
||||
break;
|
||||
case regctx_entry_t::DATA:
|
||||
value->set_bytes(p, size_in_ctx);
|
||||
break;
|
||||
case regctx_entry_t::FUNC:
|
||||
// never happens; makes compiler happy.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool patch(uint8_t *ptr, const regval_t *value) const
|
||||
{
|
||||
if ( type == regctx_entry_t::FUNC )
|
||||
{
|
||||
write_func((regctx_t *) ptr, value, user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t *p = ptr + offset_in_ctx;
|
||||
switch ( type )
|
||||
{
|
||||
case regctx_entry_t::IVAL:
|
||||
{
|
||||
uint64_t ival = value->ival;
|
||||
ival = truncate_ival(ival);
|
||||
switch ( size_in_ctx )
|
||||
{
|
||||
case 1: *(uint8_t *)p = ival; break;
|
||||
case 2: *(uint16_t *)p = ival; break;
|
||||
case 4: *(uint32_t *)p = ival; break;
|
||||
case 8: *(uint64_t *)p = ival; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case regctx_entry_t::FVAL:
|
||||
// TODO: for x86, the value is converted from IEEE format in
|
||||
// x86_write_register(). clean this up somehow.
|
||||
memcpy(p, value->fval, size_in_ctx);
|
||||
break;
|
||||
case regctx_entry_t::DATA:
|
||||
if ( value->get_data_size() != size_in_ctx )
|
||||
return false;
|
||||
memcpy(p, value->get_data(), value->get_data_size());
|
||||
break;
|
||||
case regctx_entry_t::FUNC:
|
||||
// never happens; makes compiler happy.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(regctx_entry_t);
|
||||
typedef qvector<regctx_entry_t> reg_ctx_entries_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct regctx_base_t
|
||||
{
|
||||
dynamic_register_set_t &idaregs; // linked to debmod_t's variable of the same name
|
||||
thid_t tid;
|
||||
int clsmask;
|
||||
|
||||
reg_ctx_entries_t entries;
|
||||
|
||||
void setup(thid_t _tid, int _clsmask=0)
|
||||
{
|
||||
tid = _tid;
|
||||
clsmask = _clsmask;
|
||||
}
|
||||
|
||||
void setup_reg(int dyn_reg_idx)
|
||||
{
|
||||
clsmask |= entries[dyn_reg_idx].reg_class;
|
||||
}
|
||||
|
||||
regctx_base_t(dynamic_register_set_t &_idaregs)
|
||||
: idaregs(_idaregs)
|
||||
{
|
||||
setup(0, 0);
|
||||
}
|
||||
|
||||
void add_idareg(register_info_t &ri)
|
||||
{
|
||||
idaregs.add_register(ri.name,
|
||||
ri.flags,
|
||||
ri.dtype,
|
||||
ri.register_class,
|
||||
ri.bit_strings,
|
||||
ri.default_bit_strings_mask);
|
||||
}
|
||||
|
||||
size_t add_entry(
|
||||
regctx_entry_t::type_t type,
|
||||
register_info_t &ri,
|
||||
size_t offset_in_ctx,
|
||||
size_t size_in_ctx,
|
||||
size_t reg_size)
|
||||
{
|
||||
size_t dyn_reg_idx = entries.size();
|
||||
regctx_entry_t &entry = entries.push_back();
|
||||
entry.type = type;
|
||||
entry.reg_class = ri.register_class;
|
||||
entry.reg_idx = dyn_reg_idx;
|
||||
entry.offset_in_ctx = offset_in_ctx;
|
||||
entry.size_in_ctx = size_in_ctx;
|
||||
entry.reg_size = reg_size;
|
||||
add_idareg(ri);
|
||||
return dyn_reg_idx;
|
||||
}
|
||||
|
||||
size_t add_ival(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
|
||||
{
|
||||
QASSERT(1797, size_in_ctx <= sizeof(regval_t::ival));
|
||||
size_t reg_size = ri.dtype == dt_word ? 2
|
||||
: ri.dtype == dt_dword ? 4
|
||||
: ri.dtype == dt_qword ? 8
|
||||
: 0;
|
||||
QASSERT(1798, reg_size != 0);
|
||||
return add_entry(regctx_entry_t::IVAL, ri, offset_in_ctx, size_in_ctx, reg_size);
|
||||
}
|
||||
|
||||
size_t add_fval(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
|
||||
{
|
||||
QASSERT(1799, size_in_ctx <= sizeof(regval_t::fval));
|
||||
return add_entry(regctx_entry_t::FVAL, ri, offset_in_ctx, size_in_ctx, size_in_ctx);
|
||||
}
|
||||
|
||||
size_t add_data(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
|
||||
{
|
||||
return add_entry(regctx_entry_t::DATA, ri, offset_in_ctx, size_in_ctx, size_in_ctx);
|
||||
}
|
||||
|
||||
size_t add_func(
|
||||
register_info_t &ri,
|
||||
void (*read_func)(const regctx_t *, regval_t *, void *),
|
||||
void (*write_func)(regctx_t *, const regval_t *, void *),
|
||||
void *user_data=nullptr)
|
||||
{
|
||||
size_t dyn_reg_idx = entries.size();
|
||||
regctx_entry_t &entry = entries.push_back();
|
||||
entry.type = regctx_entry_t::FUNC;
|
||||
entry.reg_class = ri.register_class;
|
||||
entry.reg_idx = dyn_reg_idx;
|
||||
entry.read_func = read_func;
|
||||
entry.write_func = write_func;
|
||||
entry.user_data = user_data;
|
||||
add_idareg(ri);
|
||||
return dyn_reg_idx;
|
||||
}
|
||||
|
||||
void read_all(regval_t *values)
|
||||
{
|
||||
for ( const regctx_entry_t &entry: entries )
|
||||
if ( (clsmask & entry.reg_class) != 0 )
|
||||
entry.read(&values[entry.reg_idx], (const uint8_t *) this);
|
||||
}
|
||||
|
||||
bool patch(int dyn_reg_idx, const regval_t *value)
|
||||
{
|
||||
return entries[dyn_reg_idx].patch((uint8_t *) this, value);
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Main class to represent a debugger module
|
||||
class debmod_t
|
||||
{
|
||||
protected:
|
||||
// processor_t &ph;
|
||||
typedef std::map<int, regval_t> regval_map_t;
|
||||
qvector<exception_info_t> exceptions;
|
||||
name_info_t dn_names;
|
||||
// Pending events. currently used only to store
|
||||
// exceptions that happen while attaching
|
||||
eventlist_t events;
|
||||
// The last event received via a successful get_debug_event()
|
||||
debug_event_t last_event;
|
||||
|
||||
// debugged process attributes (may be changed after process start/attach)
|
||||
debapp_attrs_t debapp_attrs;
|
||||
|
||||
procvec_t proclist;
|
||||
|
||||
// appcall contexts
|
||||
struct call_context_t
|
||||
{
|
||||
regvals_t saved_regs;
|
||||
ea_t sp;
|
||||
ea_t ctrl_ea;
|
||||
bool regs_spoiled;
|
||||
call_context_t() : sp(BADADDR), ctrl_ea(BADADDR), regs_spoiled(false) {}
|
||||
};
|
||||
typedef qstack<call_context_t> call_contexts_t;
|
||||
typedef std::map<thid_t, call_contexts_t> appcalls_t;
|
||||
appcalls_t appcalls;
|
||||
|
||||
int send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize)
|
||||
{
|
||||
return ::send_ioctl(rpc, fn, buf, size, poutbuf, poutsize);
|
||||
}
|
||||
// If an IDC error occurs: we cannot prepare an error message on the server
|
||||
// side because we do not have access to error strings (they are in ida.hlp).
|
||||
// We pass the error code to IDA (with eventual arguments) so it can prepare
|
||||
// a nice error message for the user
|
||||
void report_idc_error(ea_t ea, error_t code, ssize_t errval, const char *errprm)
|
||||
{
|
||||
return ::report_idc_error(rpc, ea, code, errval, errprm);
|
||||
}
|
||||
|
||||
typedef std::map<ea_t, lowcnd_t> lowcnds_t;
|
||||
lowcnds_t cndmap;
|
||||
eavec_t handling_lowcnds;
|
||||
bool evaluate_and_handle_lowcnd(debug_event_t *event, int elc_flags=0);
|
||||
bool handle_lowcnd(lowcnd_t *lc, debug_event_t *event, int elc_flags);
|
||||
#define ELC_KEEP_EIP 0x0001 // do not reset eip before stepping
|
||||
#define ELC_KEEP_SUSP 0x0002 // keep suspended state, do not resume after stepping
|
||||
|
||||
// helper functions for programmatical single stepping
|
||||
virtual drc_t dbg_perform_single_step(debug_event_t *event, const insn_t &ins);
|
||||
virtual int dbg_freeze_threads_except(thid_t) { return 0; }
|
||||
virtual int dbg_thaw_threads_except(thid_t) { return 0; }
|
||||
drc_t resume_app_and_get_event(debug_event_t *dev);
|
||||
void set_platform(const char *platform_name);
|
||||
|
||||
// return number of processes, -1 - not implemented
|
||||
virtual int idaapi get_process_list(procvec_t *proclist, qstring *errbuf);
|
||||
|
||||
public:
|
||||
// initialized by dbg_init()
|
||||
int debugger_flags;
|
||||
meminfo_vec_t old_ranges;
|
||||
rpc_engine_t *rpc;
|
||||
bool debug_debugger;
|
||||
// Is dynamic library?
|
||||
bool is_dll;
|
||||
|
||||
// indexes of sp and program counter registers.
|
||||
// Must be initialized by derived classes.
|
||||
int sp_idx;
|
||||
int pc_idx;
|
||||
|
||||
// index of frame pointer register
|
||||
int fp_idx;
|
||||
|
||||
// Total number of registers.
|
||||
// Must be initialized by derived classes.
|
||||
int nregs;
|
||||
|
||||
// Breakpoint code.
|
||||
// Must be initialized by derived classes.
|
||||
bytevec_t bpt_code;
|
||||
|
||||
qstring input_file_path;
|
||||
|
||||
page_bpts_t page_bpts;
|
||||
|
||||
// will either send as msg/warning/error through callui,
|
||||
// or through 'rpc' if it is present.
|
||||
DEFINE_ALL_NOTIFICATION_FUNCTIONS(rpc);
|
||||
AS_PRINTF(3,0) static ssize_t dvnotif(int code, rpc_engine_t *rpc, const char *format, va_list va);
|
||||
|
||||
bool broken_connection;
|
||||
pid_t pid;
|
||||
|
||||
debmodbpt_map_t bpts;
|
||||
update_bpt_vec_t deleted_bpts; // deleted bpts in the last update_bpts() call
|
||||
// if bpt EA was deleted then bpt raised in the same place for the
|
||||
// same thread does not belong to IDA
|
||||
bool is_ida_bpt(ea_t ea, thid_t tid)
|
||||
{
|
||||
if ( bpts.find(ea) != bpts.end() )
|
||||
return true;
|
||||
update_bpt_info_t b;
|
||||
b.ea = ea;
|
||||
return deleted_bpts.find(b) != deleted_bpts.end()
|
||||
&& (last_event.eid() != BREAKPOINT
|
||||
|| last_event.ea != ea
|
||||
|| last_event.tid != tid);
|
||||
}
|
||||
|
||||
static bool reuse_broken_connections;
|
||||
|
||||
//------------------------------------
|
||||
// Dynamic register set for debugger_t
|
||||
//------------------------------------
|
||||
dynamic_register_set_t idaregs;
|
||||
|
||||
//------------------------------------
|
||||
// Constructors and destructors
|
||||
//------------------------------------
|
||||
debmod_t();
|
||||
virtual ~debmod_t() { cleanup(); }
|
||||
|
||||
//------------------------------------
|
||||
// Debug names methods
|
||||
//------------------------------------
|
||||
void clear_debug_names();
|
||||
name_info_t *get_debug_names();
|
||||
void save_debug_name(ea_t ea, const char *name);
|
||||
int set_debug_names();
|
||||
int send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty);
|
||||
int send_debug_event_to_ida(const debug_event_t *ev, int rqflags);
|
||||
ea_t find_debug_name(const char *name) const;
|
||||
//------------------------------------
|
||||
// Utility methods
|
||||
//------------------------------------
|
||||
void cleanup(void);
|
||||
AS_PRINTF(2, 3) void debdeb(const char *format, ...);
|
||||
AS_PRINTF(2, 3) bool deberr(const char *format, ...);
|
||||
bool same_as_oldmemcfg(const meminfo_vec_t &ranges) const;
|
||||
void save_oldmemcfg(const meminfo_vec_t &ranges);
|
||||
bool continue_after_last_event(bool handled = true);
|
||||
lowcnd_t *get_failed_lowcnd(thid_t tid, ea_t ea);
|
||||
page_bpts_t::iterator find_page_bpt(ea_t ea, int size=1);
|
||||
bool del_page_bpt(ea_t ea, bpttype_t type);
|
||||
void enable_page_bpts(bool enable);
|
||||
ea_t calc_page_base(ea_t ea) { return align_down(ea, dbg_memory_page_size()); }
|
||||
void log_exception(const debug_event_t *ev, const exception_info_t *ei);
|
||||
uint64 probe_file_size(int fn, uint64 step);
|
||||
void set_input_path(const char *input_path);
|
||||
int read_bpt_orgbytes(bytevec_t *buf, ea_t ea, int len);
|
||||
|
||||
//------------------------------------
|
||||
// Shared methods
|
||||
//------------------------------------
|
||||
virtual bool check_input_file_crc32(uint32 orig_crc);
|
||||
virtual const exception_info_t *find_exception(int code);
|
||||
virtual bool get_exception_name(int code, char *buf, size_t bufsize);
|
||||
virtual drc_t idaapi dbg_get_processes(procinfo_vec_t *info, qstring *errbuf);
|
||||
|
||||
//------------------------------------
|
||||
// Methods to be implemented
|
||||
//------------------------------------
|
||||
virtual void idaapi dbg_set_debugging(bool _debug_debugger) = 0;
|
||||
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) = 0;
|
||||
virtual void idaapi dbg_term(void) = 0;
|
||||
virtual drc_t idaapi dbg_detach_process(void) = 0;
|
||||
virtual drc_t idaapi dbg_start_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
qstring *errbuf) = 0;
|
||||
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_msecs) = 0;
|
||||
virtual drc_t idaapi dbg_attach_process(pid_t process_id, int event_id, int flags, qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) = 0;
|
||||
virtual void idaapi dbg_set_exception_info(const exception_info_t *info, int qty);
|
||||
virtual void idaapi dbg_stopped_at_debug_event(import_infos_t *infos, bool dlls_added, thread_name_vec_t *thr_names) = 0;
|
||||
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) = 0;
|
||||
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) = 0;
|
||||
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) = 0;
|
||||
virtual drc_t idaapi dbg_read_registers(
|
||||
thid_t thread_id,
|
||||
int clsmask,
|
||||
regval_t *values,
|
||||
qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_write_register(
|
||||
thid_t thread_id,
|
||||
int reg_idx,
|
||||
const regval_t *value,
|
||||
qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) = 0;
|
||||
virtual ea_t idaapi map_address(ea_t ea, const regval_t *, int /* regnum */) { return ea; }
|
||||
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &ranges, qstring *errbuf) = 0;
|
||||
virtual int idaapi dbg_get_scattered_image(scattered_image_t & /*si*/, ea_t /*base*/) { return -1; }
|
||||
virtual bool idaapi dbg_get_image_uuid(bytevec_t * /*uuid*/, ea_t /*base*/) { return false; }
|
||||
virtual ea_t idaapi dbg_get_segm_start(ea_t /*base*/, const qstring & /*segname*/) { return BADADDR; }
|
||||
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) = 0;
|
||||
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) = 0;
|
||||
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) = 0;
|
||||
// for swbpts, len may be -1 (unknown size, for example arm/thumb mode) or bpt opcode length
|
||||
// dbg_add_bpt returns 2 if it adds a page bpt
|
||||
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) = 0;
|
||||
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) = 0;
|
||||
virtual drc_t idaapi dbg_update_bpts(int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf);
|
||||
virtual int idaapi dbg_add_page_bpt(bpttype_t /*type*/, ea_t /*ea*/, int /*size*/) { return 0; }
|
||||
virtual bool idaapi dbg_enable_page_bpt(page_bpts_t::iterator /*p*/, bool /*enable*/) { return false; }
|
||||
virtual drc_t idaapi dbg_update_lowcnds(int *nupdated, const lowcnd_t *lowcnds, int nlowcnds, qstring *errbuf);
|
||||
virtual drc_t idaapi dbg_eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf);
|
||||
virtual int idaapi dbg_open_file(const char * /*file*/, uint64 * /*fsize*/, bool /*readonly*/) { return -1; }
|
||||
virtual void idaapi dbg_close_file(int /*fn*/) {}
|
||||
virtual ssize_t idaapi dbg_read_file(int /*fn*/, qoff64_t /*off*/, void * /*buf*/, size_t /*size*/) { return 0; }
|
||||
virtual ssize_t idaapi dbg_write_file(int /*fn*/, qoff64_t /*off*/, const void * /*buf*/, size_t /*size*/) { return 0; }
|
||||
virtual int idaapi handle_ioctl(
|
||||
int /*fn*/,
|
||||
const void * /*buf*/,
|
||||
size_t /*size*/,
|
||||
void ** /*outbuf*/,
|
||||
ssize_t * /*outsize*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int idaapi get_system_specific_errno(void) const; // this code must be acceptable by winerr()
|
||||
virtual drc_t idaapi dbg_update_call_stack(thid_t, call_stack_t *) { return DRC_NONE; }
|
||||
virtual ea_t idaapi dbg_appcall(
|
||||
ea_t /*func_ea*/,
|
||||
thid_t /*tid*/,
|
||||
int /*stkarg_nbytes*/,
|
||||
const struct regobjs_t * /*regargs*/,
|
||||
struct relobj_t * /*stkargs*/,
|
||||
struct regobjs_t * /*retregs*/,
|
||||
qstring *errbuf,
|
||||
debug_event_t * /*event*/,
|
||||
int /*flags*/);
|
||||
virtual drc_t idaapi dbg_cleanup_appcall(thid_t /*tid*/);
|
||||
virtual bool idaapi write_registers(
|
||||
thid_t /*tid*/,
|
||||
int /*start*/,
|
||||
int /*count*/,
|
||||
const regval_t * /*values*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// finalize appcall stack image
|
||||
// input: stack image contains the return address at the beginning
|
||||
virtual int finalize_appcall_stack(call_context_t &, regval_map_t &, bytevec_t &) { return 0; }
|
||||
virtual ea_t calc_appcall_stack(const regvals_t ®vals);
|
||||
virtual bool should_stop_appcall(thid_t tid, const debug_event_t *event, ea_t ea);
|
||||
virtual bool should_suspend_at_exception(const debug_event_t *event, const exception_info_t *ei);
|
||||
virtual bool preprocess_appcall_cleanup(thid_t, call_context_t &) { return true; }
|
||||
virtual int get_regidx(const char *regname, int *clsmask) = 0;
|
||||
virtual uint32 dbg_memory_page_size(void) { return 0x1000; }
|
||||
virtual bool idaapi dbg_prepare_broken_connection(void) { return false; }
|
||||
virtual bool idaapi dbg_continue_broken_connection(pid_t) { old_ranges.clear(); return true; }
|
||||
virtual bool idaapi dbg_enable_trace(thid_t, bool, int) { return false; }
|
||||
virtual bool idaapi dbg_is_tracing_enabled(thid_t, int) { return false; }
|
||||
virtual int idaapi dbg_rexec(const char *cmdline);
|
||||
virtual void adjust_swbpt(ea_t *, int *) {};
|
||||
virtual void dbg_get_debapp_attrs(debapp_attrs_t *out_pattrs) const;
|
||||
virtual bool idaapi dbg_get_srcinfo_path(qstring * /*path*/, ea_t /*base*/) const { return false; }
|
||||
virtual bool import_dll(const import_request_t & /*req*/) { return false; }
|
||||
virtual drc_t idaapi dbg_bin_search(
|
||||
ea_t *ea,
|
||||
ea_t start_ea,
|
||||
ea_t end_ea,
|
||||
const compiled_binpat_vec_t &ptns,
|
||||
int srch_flags,
|
||||
qstring *errbuf);
|
||||
|
||||
bool restore_broken_breakpoints(void);
|
||||
void set_addr_size(int size) { debapp_attrs.addrsize = size; }
|
||||
void set_endianness(bool is_be) { debapp_attrs.is_be = is_be; }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Some functions, per OS implemented
|
||||
//
|
||||
bool init_subsystem();
|
||||
bool term_subsystem();
|
||||
debmod_t *create_debug_session(void *params);
|
||||
|
||||
//
|
||||
// Processor specific init/term
|
||||
//
|
||||
void processor_specific_init(void);
|
||||
void processor_specific_term(void);
|
||||
|
||||
// Perform an action on all existing debugger modules
|
||||
struct debmod_visitor_t
|
||||
{
|
||||
virtual int visit(debmod_t *debmod) = 0;
|
||||
};
|
||||
int for_all_debuggers(debmod_visitor_t &v);
|
||||
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
|
||||
// Common method between MacOS and Linux to launch a process
|
||||
drc_t idaapi maclnx_launch_process(
|
||||
debmod_t *debmod,
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
void **child_pid,
|
||||
qstring *errbuf);
|
||||
|
||||
bool add_idc_funcs(const struct ext_idcfunc_t funcs[], size_t nfuncs, bool reg);
|
||||
|
||||
//
|
||||
// Externs
|
||||
//
|
||||
extern debmod_t *idc_debmod;
|
||||
extern thid_t idc_thread;
|
||||
extern bool ignore_sigint;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// server.cpp
|
||||
bool lock_begin();
|
||||
bool lock_end();
|
||||
|
||||
// bool srv_lock_begin(void);
|
||||
// bool srv_lock_end(void);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,349 +0,0 @@
|
||||
#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 <map>
|
||||
#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);
|
||||
|
||||
// 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 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);
|
||||
void attach_to_thread(int tid, ea_t ea);
|
||||
bool check_for_new_events(chk_signal_info_t *csi, bool *event_prepared);
|
||||
void handle_extended_wait(bool *handled, const chk_signal_info_t &csi);
|
||||
|
||||
//
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,138 +0,0 @@
|
||||
#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));
|
||||
qfclose(cmdfp);
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
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 \
|
||||
symelf.hpp
|
||||
@@ -1,23 +0,0 @@
|
||||
|
||||
// This option is ideal when working with the Remote Mac OSX Debugger.
|
||||
// It allows IDA to parse symbol data for dyld_shared_cache libs locally, rather than
|
||||
// transferring symbol names over the wire, which can save a significant amount of time.
|
||||
//
|
||||
// Here's an example of how to use it:
|
||||
//
|
||||
// First download the ios_deploy utility at: https://www.hex-rays.com/products/ida/support/ida/ios_deploy.zip.
|
||||
// Then run the following commands on the remote OSX machine:
|
||||
//
|
||||
// $ mkdir ~/Symbols
|
||||
// $ ios_deploy symbols -c /var/db/dyld/dyld_shared_cache_x86_64h -d ~/Symbols
|
||||
// $ ios_deploy symbols -c /var/db/dyld/dyld_shared_cache_i386 -d ~/Symbols
|
||||
//
|
||||
// Then, on the client machine running IDA:
|
||||
//
|
||||
// $ scp -r user@mac:Symbols ~/
|
||||
// set SYMBOL_PATH = "~/Symbols" in dbg_macosx.cfg
|
||||
// start IDA and run the debugger, symbol loading should now be much faster
|
||||
//
|
||||
// Any errors will be printed to the console.
|
||||
|
||||
SYMBOL_PATH = "";
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,546 +0,0 @@
|
||||
#ifndef __MAC_DEBUGGER_MODULE__
|
||||
#define __MAC_DEBUGGER_MODULE__
|
||||
|
||||
/*
|
||||
* This is the mach (MAC OS X) debugger module
|
||||
*
|
||||
* Functions unique for Mach (MAC OS X)
|
||||
*
|
||||
*/
|
||||
#include <map>
|
||||
|
||||
#include <pro.h>
|
||||
#include <fpro.h>
|
||||
#include <err.h>
|
||||
#include <ida.hpp>
|
||||
#include <idp.hpp>
|
||||
#include <idd.hpp>
|
||||
#include <name.hpp>
|
||||
#include <bytes.hpp>
|
||||
#include <loader.hpp>
|
||||
#include <diskio.hpp>
|
||||
#include <ua.hpp>
|
||||
|
||||
#define MD msg("at line %d\n", __LINE__);
|
||||
|
||||
#define processor_t mach_processor_t
|
||||
|
||||
#include <grp.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <mach-o/reloc.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/shared_region.h>
|
||||
#include <Security/Security.h>
|
||||
#include <Security/SecCode.h> // needed for SDK versions <= 10.7
|
||||
#undef processor_t
|
||||
|
||||
#include "macbase_debmod.h"
|
||||
|
||||
typedef int HANDLE;
|
||||
class mac_debmod_t;
|
||||
|
||||
#define INVALID_HANDLE_VALUE (-1)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// DEBUGGER INTERNAL DATA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
enum run_state_t
|
||||
{
|
||||
rs_running,
|
||||
rs_pausing,
|
||||
rs_suspended, // used by iphone
|
||||
rs_exiting,
|
||||
rs_exited
|
||||
};
|
||||
|
||||
// image information
|
||||
struct image_info_t
|
||||
{
|
||||
ea_t base;
|
||||
asize_t size;
|
||||
qstring name;
|
||||
bytevec_t uuid;
|
||||
|
||||
image_info_t() { clear(); }
|
||||
image_info_t(ea_t _base, uint32 _size, const qstring &_name, const bytevec_t &_uuid)
|
||||
: base(_base), size(_size), name(_name), uuid(_uuid) {}
|
||||
|
||||
void clear()
|
||||
{
|
||||
base = BADADDR;
|
||||
size = 0;
|
||||
name.clear();
|
||||
uuid.clear();
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<ea_t, image_info_t> images_t; // key: image base address
|
||||
|
||||
union my_mach_msg_t
|
||||
{
|
||||
mach_msg_header_t hdr;
|
||||
char data[1024];
|
||||
void display(const char *header);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
enum block_type_t
|
||||
{
|
||||
bl_none, // process is running
|
||||
bl_signal, // blocked due to a signal (must say PTRACE_CONT)
|
||||
bl_exception, // blocked due to an exception (must say task_resume())
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// thread information
|
||||
//-V:ida_thread_info_t:730 Not all members of a class are initialized inside the constructor. Consider inspecting: excmsg.
|
||||
struct ida_thread_info_t
|
||||
{
|
||||
ida_thread_info_t(thid_t t, mach_port_t p)
|
||||
: tid(t), port(p), child_signum(0), asked_step(false), single_step(false),
|
||||
pending_sigstop(false), block(bl_none), run_handled(false) {}
|
||||
int tid;
|
||||
mach_port_t port;
|
||||
int child_signum;
|
||||
bool asked_step;
|
||||
bool single_step;
|
||||
bool pending_sigstop;
|
||||
block_type_t block;
|
||||
my_mach_msg_t excmsg;
|
||||
bool run_handled;
|
||||
bool blocked(void) const { return block != bl_none; }
|
||||
};
|
||||
|
||||
typedef std::map<int, ida_thread_info_t> threads_t; // (tid -> info)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct mach_exception_port_info_t
|
||||
{
|
||||
exception_mask_t masks[EXC_TYPES_COUNT];
|
||||
mach_port_t ports[EXC_TYPES_COUNT];
|
||||
exception_behavior_t behaviors[EXC_TYPES_COUNT];
|
||||
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
|
||||
mach_msg_type_number_t count;
|
||||
};
|
||||
|
||||
typedef qvector<struct nlist_64> nlists_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct mach_exception_info_t
|
||||
{
|
||||
task_t task_port;
|
||||
thread_t thread_port;
|
||||
exception_type_t exception_type;
|
||||
exception_data_t exception_data;
|
||||
mach_msg_type_number_t data_count;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
typedef janitor_t<AuthorizationRef> auth_ref_janitor_t;
|
||||
template <> inline auth_ref_janitor_t::~janitor_t()
|
||||
{
|
||||
if ( resource != NULL )
|
||||
AuthorizationFree(resource, kAuthorizationFlagDefaults);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
typedef janitor_t<AuthorizationRights *> auth_rights_janitor_t;
|
||||
template <> inline auth_rights_janitor_t::~janitor_t()
|
||||
{
|
||||
if ( resource != NULL )
|
||||
AuthorizationFreeItemSet(resource);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
typedef janitor_t<SecCodeRef> sec_code_janitor_t;
|
||||
template <> inline sec_code_janitor_t::~janitor_t()
|
||||
{
|
||||
if ( resource != NULL )
|
||||
CFRelease(resource);
|
||||
}
|
||||
|
||||
struct vm_region_visitor_t
|
||||
{
|
||||
virtual int visit_region(memory_info_t &mi) = 0;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct machine_thread_state_t
|
||||
{
|
||||
ea_t __eax;
|
||||
ea_t __ebx;
|
||||
ea_t __ecx;
|
||||
ea_t __edx;
|
||||
ea_t __edi;
|
||||
ea_t __esi;
|
||||
ea_t __ebp;
|
||||
ea_t __esp;
|
||||
ea_t __eip;
|
||||
ea_t __r8;
|
||||
ea_t __r9;
|
||||
ea_t __r10;
|
||||
ea_t __r11;
|
||||
ea_t __r12;
|
||||
ea_t __r13;
|
||||
ea_t __r14;
|
||||
ea_t __r15;
|
||||
ea_t __eflags;
|
||||
ea_t __ss;
|
||||
ea_t __cs;
|
||||
ea_t __ds;
|
||||
ea_t __es;
|
||||
ea_t __fs;
|
||||
ea_t __gs;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct machine_float_state_t
|
||||
{
|
||||
uint16 __fpu_fcw;
|
||||
uint16 __fpu_fsw;
|
||||
uint8 __fpu_ftw;
|
||||
uint16 __fpu_fop;
|
||||
uint32 __fpu_ip;
|
||||
uint16 __fpu_cs;
|
||||
uint32 __fpu_dp;
|
||||
uint16 __fpu_ds;
|
||||
uint32 __fpu_mxcsr;
|
||||
uint32 __fpu_mxcsrmask;
|
||||
|
||||
_STRUCT_MMST_REG __fpu_stmm0;
|
||||
_STRUCT_MMST_REG __fpu_stmm1;
|
||||
_STRUCT_MMST_REG __fpu_stmm2;
|
||||
_STRUCT_MMST_REG __fpu_stmm3;
|
||||
_STRUCT_MMST_REG __fpu_stmm4;
|
||||
_STRUCT_MMST_REG __fpu_stmm5;
|
||||
_STRUCT_MMST_REG __fpu_stmm6;
|
||||
_STRUCT_MMST_REG __fpu_stmm7;
|
||||
|
||||
_STRUCT_XMM_REG __fpu_xmm0;
|
||||
_STRUCT_XMM_REG __fpu_xmm1;
|
||||
_STRUCT_XMM_REG __fpu_xmm2;
|
||||
_STRUCT_XMM_REG __fpu_xmm3;
|
||||
_STRUCT_XMM_REG __fpu_xmm4;
|
||||
_STRUCT_XMM_REG __fpu_xmm5;
|
||||
_STRUCT_XMM_REG __fpu_xmm6;
|
||||
_STRUCT_XMM_REG __fpu_xmm7;
|
||||
_STRUCT_XMM_REG __fpu_xmm8;
|
||||
_STRUCT_XMM_REG __fpu_xmm9;
|
||||
_STRUCT_XMM_REG __fpu_xmm10;
|
||||
_STRUCT_XMM_REG __fpu_xmm11;
|
||||
_STRUCT_XMM_REG __fpu_xmm12;
|
||||
_STRUCT_XMM_REG __fpu_xmm13;
|
||||
_STRUCT_XMM_REG __fpu_xmm14;
|
||||
_STRUCT_XMM_REG __fpu_xmm15;
|
||||
|
||||
_STRUCT_XMM_REG __fpu_ymmh0;
|
||||
_STRUCT_XMM_REG __fpu_ymmh1;
|
||||
_STRUCT_XMM_REG __fpu_ymmh2;
|
||||
_STRUCT_XMM_REG __fpu_ymmh3;
|
||||
_STRUCT_XMM_REG __fpu_ymmh4;
|
||||
_STRUCT_XMM_REG __fpu_ymmh5;
|
||||
_STRUCT_XMM_REG __fpu_ymmh6;
|
||||
_STRUCT_XMM_REG __fpu_ymmh7;
|
||||
_STRUCT_XMM_REG __fpu_ymmh8;
|
||||
_STRUCT_XMM_REG __fpu_ymmh9;
|
||||
_STRUCT_XMM_REG __fpu_ymmh10;
|
||||
_STRUCT_XMM_REG __fpu_ymmh11;
|
||||
_STRUCT_XMM_REG __fpu_ymmh12;
|
||||
_STRUCT_XMM_REG __fpu_ymmh13;
|
||||
_STRUCT_XMM_REG __fpu_ymmh14;
|
||||
_STRUCT_XMM_REG __fpu_ymmh15;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct machine_debug_state_t
|
||||
{
|
||||
ea_t __dr0;
|
||||
ea_t __dr1;
|
||||
ea_t __dr2;
|
||||
ea_t __dr3;
|
||||
ea_t __dr4;
|
||||
ea_t __dr5;
|
||||
ea_t __dr6;
|
||||
ea_t __dr7;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class mac_debmod_t: public macbase_debmod_t
|
||||
{
|
||||
typedef macbase_debmod_t inherited;
|
||||
public:
|
||||
procinfo_vec_t processes;
|
||||
|
||||
// debugged process information
|
||||
mach_port_t task; // debugged application's task port
|
||||
|
||||
cpu_type_t cputype; // process' CPU type (e.g. CPU_TYPE_I386 or CPU_TYPE_X86_64)
|
||||
|
||||
bool in_ptrace; // We use ptrace to start the debugging session
|
||||
// but since it is badly broken, we detach and
|
||||
// revert to low-level mach api immediately after that
|
||||
|
||||
run_state_t run_state;
|
||||
|
||||
dyld_utils_t dyld;
|
||||
|
||||
image_info_t exeimg; // image info for the exe module
|
||||
|
||||
images_t dlls; // list of loaded dynamic libraries
|
||||
|
||||
easet_t dlls_to_import; // list of dlls to import information from
|
||||
|
||||
inline bool exited(void)
|
||||
{
|
||||
return run_state == rs_exited;
|
||||
}
|
||||
|
||||
threads_t threads;
|
||||
|
||||
struct stored_signal_t
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
};
|
||||
typedef qvector<stored_signal_t> stored_signals_t;
|
||||
static stored_signals_t pending_signals; // signals retrieved by other threads
|
||||
|
||||
bool attaching; // Handling events linked to PTRACE_ATTACH, don't run the program yet
|
||||
bool is64; // is target 64-bit?
|
||||
|
||||
mach_port_t exc_port;
|
||||
mach_exception_port_info_t saved_exceptions;
|
||||
|
||||
regctx_t *reg_ctx;
|
||||
|
||||
mac_debmod_t();
|
||||
~mac_debmod_t();
|
||||
|
||||
void init_reg_ctx(void);
|
||||
void term_reg_ctx(void);
|
||||
|
||||
void handle_dyld_bpt(const debug_event_t *event);
|
||||
bool retrieve_pending_signal(int *status);
|
||||
kern_return_t read_mem(ea_t ea, void *buffer, int size, int *read_size);
|
||||
void unblock_all_threads();
|
||||
void resume_all_threads();
|
||||
bool suspend_all_threads();
|
||||
bool my_resume_thread(ida_thread_info_t &ti);
|
||||
pid_t qwait(int *status, bool hang);
|
||||
void get_debug_events(int timeout_ms);
|
||||
kern_return_t
|
||||
catch_exception_raise(mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code_vector,
|
||||
mach_msg_type_number_t code_count);
|
||||
ea_t get_ip(thid_t tid);
|
||||
uval_t get_dr(thid_t tid, int idx);
|
||||
bool set_dr(thid_t tid, int idx, uval_t value);
|
||||
bool idaapi thread_get_fs_base(thid_t tid, int reg_idx, ea_t *pea);
|
||||
bool parse_macho_image(macho_visitor_t &mv, const image_info_t &ii);
|
||||
void clean_stack_regions(meminfo_vec_t &miv) const;
|
||||
drc_t get_memory_info(meminfo_vec_t &miv, bool suspend);
|
||||
void init_dyld(void);
|
||||
void init_exeimg(pid_t _pid, thid_t tid);
|
||||
void update_dyld(void);
|
||||
bool exist_dll(const images_t &images, ea_t base);
|
||||
virtual bool refresh_hwbpts() override;
|
||||
virtual bool set_hwbpts(int hThread) newapi;
|
||||
bool handle_process_start(pid_t _pid);
|
||||
void term_exception_ports(void);
|
||||
void init_exception_ports(void);
|
||||
thid_t init_main_thread(bool reattaching);
|
||||
bool update_threads(void);
|
||||
bool thread_exit_event_planned(thid_t tid);
|
||||
void cleanup(void);
|
||||
bool xfer_memory(ea_t ea, void *buffer, int size, bool write);
|
||||
void add_dll(const image_info_t &ii);
|
||||
int _write_memory(ea_t ea, const void *buffer, int size, bool suspend=false);
|
||||
int _read_memory(ea_t ea, void *buffer, int size, bool suspend=false);
|
||||
bool xfer_page(ea_t ea, void *buffer, int size, bool write);
|
||||
kern_return_t write_mem(ea_t ea, void *buffer, int size);
|
||||
int exception_to_signal(const mach_exception_info_t *exinf);
|
||||
bool check_for_exception(int timeout, mach_exception_info_t *exinf);
|
||||
bool handle_signal(
|
||||
int code,
|
||||
debug_event_t *event,
|
||||
block_type_t block,
|
||||
const my_mach_msg_t *excmsg);
|
||||
bool check_for_exception(
|
||||
int timeout,
|
||||
mach_exception_info_t *exinf,
|
||||
my_mach_msg_t *excmsg);
|
||||
bool is_task_valid(task_t task);
|
||||
int32 qptrace(int request, pid_t pid, caddr_t addr, int data);
|
||||
ida_thread_info_t *get_thread(thid_t tid);
|
||||
int handle_bpts(debug_event_t *event, bool asked_step);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#define DEFINE_GET_STATE_FUNC(name, type, flavor, flavor_count) \
|
||||
bool name(thid_t tid, type *state) \
|
||||
{ \
|
||||
ida_thread_info_t *ti = get_thread(tid); \
|
||||
if ( ti == NULL ) \
|
||||
return false; \
|
||||
mach_port_t port = ti->port; \
|
||||
mach_msg_type_number_t stateCount = flavor_count; \
|
||||
kern_return_t err; \
|
||||
err = thread_get_state(port, \
|
||||
flavor, \
|
||||
(thread_state_t)state, \
|
||||
&stateCount); \
|
||||
QASSERT(30105, stateCount == flavor_count); \
|
||||
if ( err != KERN_SUCCESS ) \
|
||||
{ \
|
||||
debdeb("tid=%d port=%d: " #name ": %s\n", tid, port, mach_error_string(err)); \
|
||||
return false; \
|
||||
} \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define DEFINE_SET_STATE_FUNC(name, type, flavor, flavor_count) \
|
||||
bool name(thid_t tid, const type *state) \
|
||||
{ \
|
||||
ida_thread_info_t *ti = get_thread(tid); \
|
||||
if ( ti == NULL ) \
|
||||
return false; \
|
||||
mach_port_t port = ti->port; \
|
||||
mach_msg_type_number_t stateCount = flavor_count; \
|
||||
kern_return_t err; \
|
||||
err = thread_set_state(port, \
|
||||
flavor, \
|
||||
(thread_state_t)state, \
|
||||
stateCount); \
|
||||
QASSERT(30106, stateCount == flavor_count); \
|
||||
return err == KERN_SUCCESS; \
|
||||
}
|
||||
|
||||
DEFINE_GET_STATE_FUNC(get_thread_state64, x86_thread_state64_t, x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT)
|
||||
DEFINE_SET_STATE_FUNC(set_thread_state64, x86_thread_state64_t, x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT)
|
||||
DEFINE_GET_STATE_FUNC(get_thread_state32, x86_thread_state32_t, x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT)
|
||||
DEFINE_SET_STATE_FUNC(set_thread_state32, x86_thread_state32_t, x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT)
|
||||
DEFINE_GET_STATE_FUNC(get_float_state64, x86_avx_state64_t, x86_AVX_STATE64, x86_AVX_STATE64_COUNT)
|
||||
DEFINE_SET_STATE_FUNC(set_float_state64, x86_avx_state64_t, x86_AVX_STATE64, x86_AVX_STATE64_COUNT)
|
||||
DEFINE_GET_STATE_FUNC(get_float_state32, x86_avx_state32_t, x86_AVX_STATE32, x86_AVX_STATE32_COUNT)
|
||||
DEFINE_SET_STATE_FUNC(set_float_state32, x86_avx_state32_t, x86_AVX_STATE32, x86_AVX_STATE32_COUNT)
|
||||
DEFINE_GET_STATE_FUNC(get_debug_state64, x86_debug_state64_t, x86_DEBUG_STATE64, x86_DEBUG_STATE64_COUNT)
|
||||
DEFINE_SET_STATE_FUNC(set_debug_state64, x86_debug_state64_t, x86_DEBUG_STATE64, x86_DEBUG_STATE64_COUNT)
|
||||
DEFINE_GET_STATE_FUNC(get_debug_state32, x86_debug_state32_t, x86_DEBUG_STATE32, x86_DEBUG_STATE32_COUNT)
|
||||
DEFINE_SET_STATE_FUNC(set_debug_state32, x86_debug_state32_t, x86_DEBUG_STATE32, x86_DEBUG_STATE32_COUNT)
|
||||
|
||||
bool get_thread_state(thid_t tid, machine_thread_state_t *state);
|
||||
bool set_thread_state(thid_t tid, const machine_thread_state_t *state);
|
||||
bool get_float_state(thid_t tid, machine_float_state_t *state);
|
||||
bool set_float_state(thid_t tid, const machine_float_state_t *state);
|
||||
bool get_debug_state(thid_t tid, machine_debug_state_t *state);
|
||||
bool set_debug_state(thid_t tid, const machine_debug_state_t *state);
|
||||
|
||||
bool qthread_setsinglestep(ida_thread_info_t &ti);
|
||||
|
||||
bool patch_reg_context(
|
||||
machine_thread_state_t *cpu,
|
||||
machine_float_state_t *fpu,
|
||||
int reg_idx,
|
||||
const regval_t *value) const;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline thid_t maintid(void)
|
||||
{
|
||||
return threads.begin()->first;
|
||||
}
|
||||
|
||||
void create_process_start_event(pid_t pid, thid_t tid);
|
||||
void create_process_attach_event(pid_t pid);
|
||||
|
||||
//
|
||||
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 &miv, qstring *errbuf) override;
|
||||
virtual int idaapi dbg_get_scattered_image(scattered_image_t &sci, 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_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 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;
|
||||
virtual int dbg_thaw_threads_except(thid_t tid) override;
|
||||
|
||||
virtual bool idaapi dbg_continue_broken_connection(pid_t pid) override;
|
||||
virtual bool idaapi dbg_prepare_broken_connection(void) override;
|
||||
|
||||
void get_image_meminfo(meminfo_vec_t &out, const image_info_t &ii);
|
||||
|
||||
int get_task_suspend_count(void);
|
||||
|
||||
int visit_vm_regions(vm_region_visitor_t &rv);
|
||||
|
||||
static bool acquire_taskport_right();
|
||||
static bool verify_code_signature();
|
||||
static bool verify_user_privilege();
|
||||
|
||||
bool import_symbols(const image_info_t &ii);
|
||||
|
||||
virtual bool import_dll(const import_request_t &req) override;
|
||||
|
||||
bool get_image_info(image_info_t *ii, ea_t base) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>OS X Remote Debug Server (32-bit) for IDA, Copyright (c) 2015 Hex-Rays SA</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>mac_server</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.hexrays.mac_server</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>mac_server</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>SecTaskAccess</key>
|
||||
<array>
|
||||
<string>allowed</string>
|
||||
<string>debug</string>
|
||||
<string>safe</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>OS X Remote Debug Server (64-bit) for IDA, Copyright (c) 2015 Hex-Rays SA</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>mac_server64</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.hexrays.mac_serverx64</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>mac_serverx64</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>SecTaskAccess</key>
|
||||
<array>
|
||||
<string>allowed</string>
|
||||
<string>debug</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,203 +0,0 @@
|
||||
#include <loader.hpp>
|
||||
|
||||
#include "macho_rebase.cpp"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// 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)
|
||||
{
|
||||
// not a shared cache lib: it's safe to just use the imagebase
|
||||
ea_t base = get_imagebase();
|
||||
if ( base == 0 )
|
||||
{
|
||||
// old databases don't have it set; use info from netnode
|
||||
netnode n(MACHO_NODE);
|
||||
if ( exist(n) )
|
||||
base = n.altval(MACHO_ALT_IMAGEBASE);
|
||||
}
|
||||
|
||||
if ( base != BADADDR
|
||||
&& new_base != BADADDR
|
||||
&& base != new_base
|
||||
&& !rebase_scattered_segments(new_base) )
|
||||
{
|
||||
rebase_or_warn(base, new_base);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
enum macopt_idx_t
|
||||
{
|
||||
MAC_OPT_SYMBOL_PATH // path to symbols extracted from dyld shared cache
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct mac_cfgopt_t
|
||||
{
|
||||
const char *name;
|
||||
char type;
|
||||
char index;
|
||||
void *var;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
//lint -esym(843, g_must_save_cfg) could be declared as const
|
||||
static bool g_must_save_cfg = false;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static const mac_cfgopt_t g_cfgopts[] =
|
||||
{
|
||||
{ "SYMBOL_PATH", IDPOPT_STR, MAC_OPT_SYMBOL_PATH, &g_dbgmod.dyld.symbol_path, 0 },
|
||||
};
|
||||
CASSERT(IS_QSTRING(g_dbgmod.dyld.symbol_path));
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static const mac_cfgopt_t *find_option(const char *name)
|
||||
{
|
||||
for ( int i=0; i < qnumber(g_cfgopts); i++ )
|
||||
if ( strcmp(g_cfgopts[i].name, name) == 0 )
|
||||
return &g_cfgopts[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void load_mac_options()
|
||||
{
|
||||
if ( !netnode::inited() )
|
||||
return;
|
||||
|
||||
netnode node(MAC_NODE);
|
||||
if ( !exist(node) )
|
||||
return;
|
||||
|
||||
for ( int i = 0; i < qnumber(g_cfgopts); i++ )
|
||||
{
|
||||
const mac_cfgopt_t &opt = g_cfgopts[i];
|
||||
if ( opt.type == IDPOPT_STR )
|
||||
node.supstr((qstring *)opt.var, opt.index);
|
||||
else
|
||||
node.supval(opt.index, opt.var, opt.size);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void save_mac_options()
|
||||
{
|
||||
if ( !g_must_save_cfg || !netnode::inited() )
|
||||
return;
|
||||
|
||||
netnode node;
|
||||
node.create(MAC_NODE);
|
||||
if ( node != BADNODE )
|
||||
{
|
||||
for ( int i = 0; i < qnumber(g_cfgopts); i++ )
|
||||
{
|
||||
const mac_cfgopt_t &opt = g_cfgopts[i];
|
||||
if ( opt.type == IDPOPT_STR )
|
||||
node.supset(opt.index, ((qstring *)opt.var)->c_str(), 0);
|
||||
else
|
||||
node.supset(opt.index, opt.var, opt.size);
|
||||
}
|
||||
}
|
||||
|
||||
g_must_save_cfg = false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
const char *idaapi set_mac_options(const char *keyword, int pri, int value_type, const void *value)
|
||||
{
|
||||
if ( keyword == NULL )
|
||||
{
|
||||
static const char form[] =
|
||||
"Mac OSX Debugger Options\n%/"
|
||||
"<#Path to symbol files extracted from dyld_shared_cache#~S~ymbol path:q:1023:60::>\n";
|
||||
|
||||
qstring path = g_dbgmod.dyld.symbol_path;
|
||||
if ( !ask_form(form, NULL, &path) )
|
||||
return IDPOPT_OK;
|
||||
|
||||
g_dbgmod.dyld.symbol_path = path;
|
||||
g_must_save_cfg = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( *keyword == '\0' )
|
||||
{
|
||||
load_mac_options();
|
||||
return IDPOPT_OK;
|
||||
}
|
||||
|
||||
const mac_cfgopt_t *opt = find_option(keyword);
|
||||
if ( opt == NULL )
|
||||
return IDPOPT_BADKEY;
|
||||
if ( opt->type != value_type )
|
||||
return IDPOPT_BADTYPE;
|
||||
|
||||
if ( opt->type == IDPOPT_STR )
|
||||
{
|
||||
qstring *pvar = (qstring *)opt->var;
|
||||
*pvar = (char *)value;
|
||||
}
|
||||
|
||||
if ( pri == IDPOPT_PRI_HIGH )
|
||||
g_must_save_cfg = true;
|
||||
}
|
||||
|
||||
return IDPOPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static ssize_t idaapi ui_callback(void *, int notification_code, va_list)
|
||||
{
|
||||
if ( notification_code == ui_saving )
|
||||
save_mac_options();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool init_plugin(void)
|
||||
{
|
||||
#ifndef RPC_CLIENT
|
||||
if ( !init_subsystem() )
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if ( !netnode::inited() || is_miniidb() || inf_is_snapshot() )
|
||||
{
|
||||
#ifdef __MAC__
|
||||
// local debugger is available if we are running under MAC OS X
|
||||
return true;
|
||||
#else
|
||||
// for other systems only the remote debugger is available
|
||||
return debugger.is_remote();
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( inf_get_filetype() != S_FILETYPE ) // only Mach-O files
|
||||
return false;
|
||||
processor_t &ph = PH;
|
||||
if ( ph.id != TARGET_PROCESSOR && ph.id != -1 )
|
||||
return false;
|
||||
|
||||
hook_to_notification_point(HT_UI, ui_callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline void term_plugin(void)
|
||||
{
|
||||
#ifndef RPC_CLIENT
|
||||
term_subsystem();
|
||||
#endif
|
||||
unhook_from_notification_point(HT_UI, ui_callback);
|
||||
save_mac_options();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static const char comment[] = "Userland Mac OS X debugger plugin.";
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
This is the MAC OS X x86 user land debugger entry point file
|
||||
*/
|
||||
#ifndef __GNUC__
|
||||
//lint -esym(750, __LITTLE_ENDIAN__) not referenced
|
||||
#define __LITTLE_ENDIAN__
|
||||
#endif
|
||||
//#define __inline__ inline
|
||||
#define REMOTE_DEBUGGER
|
||||
#define RPC_CLIENT
|
||||
|
||||
static const char wanted_name[] = "Remote Mac OS X debugger";
|
||||
#define DEBUGGER_NAME "macosx"
|
||||
#define PROCESSOR_NAME "metapc"
|
||||
#define DEFAULT_PLATFORM_NAME "macosx"
|
||||
#define TARGET_PROCESSOR PLFM_386
|
||||
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_MACOSX_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_MACHO
|
||||
#define SET_DBG_OPTIONS set_mac_options
|
||||
#define MAC_NODE "$ remote mac options"
|
||||
|
||||
#include <pro.h>
|
||||
#include <idp.hpp>
|
||||
#include <idd.hpp>
|
||||
#include <ua.hpp>
|
||||
#include <range.hpp>
|
||||
#include <loader.hpp>
|
||||
#include <kernwin.hpp>
|
||||
#include <name.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
#include "dbg_rpc_client.h"
|
||||
#include "rpc_debmod.h"
|
||||
#include "symmacho.hpp"
|
||||
|
||||
class rstub_debmod_t : public rpc_debmod_t
|
||||
{
|
||||
typedef rpc_debmod_t inherited;
|
||||
|
||||
public:
|
||||
dyld_utils_t dyld;
|
||||
|
||||
rstub_debmod_t() : inherited(DEFAULT_PLATFORM_NAME), dyld(this, TARGET_PROCESSOR) {}
|
||||
|
||||
// handle an RPC_IMPORT_DLL request from the server. see SYMBOL_PATH in dbg_macosx.cfg.
|
||||
virtual bool import_dll(const import_request_t &req) override
|
||||
{
|
||||
dyld.update_bitness();
|
||||
struct ida_local dll_importer_t : public macho_visitor_t
|
||||
{
|
||||
dll_importer_t() : macho_visitor_t(MV_SYMBOLS) {}
|
||||
void visit_symbol(ea_t ea, const char *name) override
|
||||
{
|
||||
set_debug_name(ea, name);
|
||||
}
|
||||
};
|
||||
dll_importer_t di;
|
||||
return dyld.parse_local_symbol_file(req.base, req.path.c_str(), req.uuid, di);
|
||||
}
|
||||
};
|
||||
|
||||
rstub_debmod_t g_dbgmod;
|
||||
#include "common_stub_impl.cpp"
|
||||
|
||||
#include "pc_local_impl.cpp"
|
||||
#include "mac_local_impl.cpp"
|
||||
#include "common_local_impl.cpp"
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
This is the MAC user land local debugger entry point file
|
||||
It declares a MAC debugger module and uses the common plugin functions to build the debugger
|
||||
*/
|
||||
|
||||
static const char wanted_name[] = "Local Mac OS X debugger";
|
||||
#define DEBUGGER_NAME "macosx"
|
||||
#define PROCESSOR_NAME "metapc"
|
||||
#define TARGET_PROCESSOR PLFM_386
|
||||
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_MACOSX_USER
|
||||
#define DEBUGGER_FLAGS (DBG_FLAG_LOWCNDS \
|
||||
| DBG_FLAG_DEBTHREAD)
|
||||
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
|
||||
#define HAVE_APPCALL
|
||||
#define S_FILETYPE f_MACHO
|
||||
#define SET_DBG_OPTIONS set_mac_options
|
||||
#define MAC_NODE "$ local mac options"
|
||||
|
||||
#include <pro.h>
|
||||
#include <idd.hpp>
|
||||
#include <ua.hpp>
|
||||
#include <range.hpp>
|
||||
#include <loader.hpp>
|
||||
#include "mac_debmod.h"
|
||||
|
||||
mac_debmod_t g_dbgmod;
|
||||
#include "common_stub_impl.cpp"
|
||||
|
||||
#include "pc_local_impl.cpp"
|
||||
#include "mac_local_impl.cpp"
|
||||
#include "common_local_impl.cpp"
|
||||
@@ -1,164 +0,0 @@
|
||||
#undef processor_t
|
||||
#define processor_t mach_processor_t
|
||||
#include <sys/sysctl.h>
|
||||
#include <mach/mach.h>
|
||||
#undef processor_t
|
||||
|
||||
#include <fpro.h>
|
||||
#include <prodir.h>
|
||||
#include <diskio.hpp>
|
||||
#include "macbase_debmod.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline const char *str_bitness(int bitness)
|
||||
{
|
||||
switch ( bitness )
|
||||
{
|
||||
case 8:
|
||||
return "[64]";
|
||||
case 4:
|
||||
return "[32]";
|
||||
default:
|
||||
return "[?]";
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
cpu_type_t macbase_debmod_t::get_process_cpu(pid_t _pid) const
|
||||
{
|
||||
int mib[CTL_MAXNAME];
|
||||
size_t mibLen = CTL_MAXNAME;
|
||||
int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen);
|
||||
if ( err == 0 )
|
||||
{
|
||||
QASSERT(895, mibLen < CTL_MAXNAME);
|
||||
mib[mibLen] = _pid;
|
||||
mibLen += 1;
|
||||
cpu_type_t cpu_type;
|
||||
size_t cpuTypeSize = sizeof(cpu_type);
|
||||
err = sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0);
|
||||
if ( err == 0 )
|
||||
return cpu_type;
|
||||
}
|
||||
msg("error from sysctl: %s\n", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void build_process_ext_name(ext_process_info_t *pinfo)
|
||||
{
|
||||
pinfo->ext_name = str_bitness(pinfo->addrsize);
|
||||
pinfo->ext_name.append(' ');
|
||||
pinfo->ext_name.append(pinfo->name);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Returns the file name assciated with pid
|
||||
bool idaapi macbase_debmod_t::get_exec_fname(
|
||||
int _pid,
|
||||
char *buf,
|
||||
size_t bufsize)
|
||||
{
|
||||
int mib[3];
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_ARGMAX;
|
||||
|
||||
int argmax = 0;
|
||||
size_t size = sizeof(argmax);
|
||||
|
||||
sysctl(mib, 2, &argmax, &size, NULL, 0);
|
||||
if ( argmax <= 0 )
|
||||
argmax = QMAXPATH;
|
||||
|
||||
char *args = (char *)qalloc(argmax);
|
||||
if ( args == NULL )
|
||||
nomem("get_exec_fname");
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROCARGS2;
|
||||
mib[2] = _pid;
|
||||
|
||||
// obtain the arguments for the target process. this will
|
||||
// only work for processes that belong to the current uid,
|
||||
// so if you want it to work universally, you need to run
|
||||
// as root.
|
||||
size = argmax;
|
||||
buf[0] = '\0';
|
||||
if ( sysctl(mib, 3, args, &size, NULL, 0) != -1 )
|
||||
{
|
||||
char *ptr = args + sizeof(int);
|
||||
// show_hex(ptr, size, "procargs2\n");
|
||||
|
||||
qstrncpy(buf, ptr, bufsize);
|
||||
}
|
||||
qfree(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
|
||||
int idaapi macbase_debmod_t::get_process_bitness(int _pid)
|
||||
{
|
||||
return get_cpu_bitness(get_process_cpu(_pid));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi macbase_debmod_t::get_process_list(procvec_t *list, qstring *)
|
||||
{
|
||||
list->clear();
|
||||
int mypid = getpid();
|
||||
int sysControl[4];
|
||||
sysControl[0] = CTL_KERN;
|
||||
sysControl[1] = KERN_PROC;
|
||||
sysControl[2] = KERN_PROC_ALL;
|
||||
|
||||
qvector<struct kinfo_proc> info;
|
||||
size_t length;
|
||||
int count = 0;
|
||||
int rc = -1;
|
||||
for ( int tries=0; rc != 0 && tries < 5; ++tries )
|
||||
{
|
||||
// the first call of sysctl() is used to determine the size of the buffer
|
||||
// will be passed to the second call
|
||||
length = 0;
|
||||
sysctl(sysControl, 3, NULL, &length, NULL, 0);
|
||||
|
||||
// If the number of processes is greater than the size of the buffer
|
||||
// sysctl() supplies as much data as fits in the buffer and returns ENOMEM.
|
||||
// We reserve 100 extra elements for processes started after 1st sysctl
|
||||
// In case even this number is not sufficient we turn to the next attempt
|
||||
count = (length / sizeof (info[0])) + 100;
|
||||
if ( count <= 0 ) //-V547 is always false.
|
||||
return 0;
|
||||
if ( info.size() < count )
|
||||
info.resize(count);
|
||||
length = sizeof(info[0]) * info.size();
|
||||
rc = sysctl(sysControl, 3, info.begin(), &length, NULL, 0);
|
||||
if ( rc != 0 && errno != ENOMEM )
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = (length / sizeof (info[0])); // exact number of processes
|
||||
for ( int i=0; i < count; i++ )
|
||||
{
|
||||
extern_proc &ep = info[i].kp_proc;
|
||||
pid_t _pid = ep.p_pid;
|
||||
if ( _pid == mypid )
|
||||
continue;
|
||||
mach_port_t port;
|
||||
kern_return_t result = task_for_pid(mach_task_self(), _pid, &port);
|
||||
if ( result == KERN_SUCCESS )
|
||||
{
|
||||
ext_process_info_t &pi = list->push_back();
|
||||
pi.name = ep.p_comm;
|
||||
pi.pid = _pid;
|
||||
pi.addrsize = get_process_bitness(_pid);
|
||||
build_process_ext_name(&pi);
|
||||
}
|
||||
else
|
||||
{
|
||||
debdeb("%d: %s is unavailable for debugging\n", _pid, info[i].kp_proc.p_comm);
|
||||
}
|
||||
}
|
||||
return list->size();
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#ifndef __MACBASE_HPP__
|
||||
#define __MACBASE_HPP__
|
||||
|
||||
#include "debmod.h"
|
||||
|
||||
#include "pc_debmod.h"
|
||||
#define BASE_DEBUGGER_MODULE pc_debmod_t
|
||||
|
||||
#ifndef __LINUX__ // linux gcc cannot compile macho-o headers
|
||||
#include "symmacho.hpp"
|
||||
#endif
|
||||
|
||||
// avoid conflicts with audit.h:
|
||||
#define token_t __mac_token_t
|
||||
#include <sys/sysctl.h>
|
||||
#include <mach-o/fat.h>
|
||||
#undef token_t
|
||||
|
||||
class macbase_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;
|
||||
|
||||
cpu_type_t get_process_cpu(pid_t pid) const;
|
||||
inline int idaapi get_cpu_bitness(cpu_type_t cpu);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline int idaapi macbase_debmod_t::get_cpu_bitness(cpu_type_t cpu)
|
||||
{
|
||||
return (cpu & CPU_ARCH_ABI64) != 0 ? 8 : 4;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,350 +0,0 @@
|
||||
include ../../allmake.mak
|
||||
|
||||
GOALS-$(BUILD_IDA) += configs # target in $(IDA)module.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 __MAC__
|
||||
SERVER = mac_server$(B)
|
||||
endif
|
||||
ifdef SERVER
|
||||
SERVERS += $(call server_exe,$(SERVER))
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
STUB = $(call module_dll,mac_stub)
|
||||
ifdef BUILD_IDA
|
||||
MODULES += $(STUB)
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
USER = $(call module_dll,mac_user)
|
||||
ifeq ($(and $(BUILD_IDA),$(__MAC__)),1)
|
||||
MODULES += $(USER)
|
||||
CONFIGS += dbg_macosx.cfg
|
||||
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-$(__MAC__) += $(F)macbase_debmod$(O)
|
||||
BASE_OBJS-$(__MAC__) += $(F)mac_debmod$(O)
|
||||
BASE_OBJS-$(__MAC__) += $(F)excServer$(O)
|
||||
|
||||
BASE_OBJS += $(BASE_OBJS-1) $(F)symmacho$(O)
|
||||
|
||||
$(F)symmacho$(O): CC_INCP += ../../ldr/mach-o/h $(IRRXML)
|
||||
$(F)mac_debmod$(O): CC_INCP += ../../ldr/mach-o/h
|
||||
|
||||
excServer.c:
|
||||
mig $(MACSDK)/usr/include/mach/exc.defs
|
||||
ifdef __LINT__
|
||||
$(F)excServer$(O):
|
||||
touch $@
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
SERVER_LDFLAGS += $(OUTMAP)$(F)$(@F).map
|
||||
SERVER_LDFLAGS += -Wl,-sectcreate,__TEXT,__info_plist,mac_debug$(B).plist
|
||||
|
||||
SERVER_STDLIBS += -framework CoreFoundation
|
||||
SERVER_STDLIBS += -framework Security
|
||||
|
||||
# NOTE: perform the steps in mac_server_certificate.txt before using CODESIGN_MAC_SERVER!
|
||||
ifdef CODESIGN_MAC_SERVER
|
||||
CODESIGN_IDENTITY ?= mac_server
|
||||
ifdef MAC_KEYCHAIN
|
||||
CODESIGN_OPTS = --keychain $(MAC_KEYCHAIN).keychain
|
||||
endif
|
||||
ifdef MAC_TIMESTAMP
|
||||
CODESIGN_OPTS += --timestamp=$(MAC_TIMESTAMP)
|
||||
endif
|
||||
ifndef __CODE_CHECKER__
|
||||
SERVER_POSTACTION = $(strip codesign $(CODESIGN_OPTS) -s "$(CODESIGN_IDENTITY)" $@)
|
||||
endif
|
||||
endif
|
||||
SERVER_OBJS += $(BASE_OBJS)
|
||||
|
||||
include ../server.mak
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
STUB_OBJS += $(F)mac_stub$(O)
|
||||
STUB_OBJS += $(F)symmacho$(O)
|
||||
$(STUB): MODULE_OBJS += $(STUB_OBJS)
|
||||
$(STUB): $(STUB_OBJS)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
USER_OBJS += $(F)mac_user$(O)
|
||||
USER_OBJS += $(BASE_OBJS)
|
||||
$(USER): MODULE_OBJS += $(USER_OBJS)
|
||||
$(USER): $(USER_OBJS)
|
||||
$(USER): STDLIBS += -framework Security
|
||||
$(USER): STDLIBS += -framework CoreFoundation
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
include $(IDA)objdir.mak
|
||||
|
||||
# MAKEDEP dependency list ------------------
|
||||
$(F)excServer$(O): ../../ldr/mach-o/h/arm/_types.h \
|
||||
../../ldr/mach-o/h/i386/_types.h \
|
||||
../../ldr/mach-o/h/mach/arm/boolean.h \
|
||||
../../ldr/mach-o/h/mach/arm/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/machine/boolean.h \
|
||||
../../ldr/mach-o/h/mach/machine/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/machine/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/message.h \
|
||||
../../ldr/mach-o/h/mach/port.h \
|
||||
../../ldr/mach-o/h/mach/ppc/boolean.h \
|
||||
../../ldr/mach-o/h/mach/ppc/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_types.h \
|
||||
../../ldr/mach-o/h/ppc/_types.h \
|
||||
../../ldr/mach-o/h/sys/_posix_availability.h \
|
||||
../../ldr/mach-o/h/sys/_symbol_aliasing.h \
|
||||
../../ldr/mach-o/h/sys/cdefs.h excServer.c
|
||||
$(F)mac_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)range.hpp $(I)segment.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../../ldr/mach-o/common.h \
|
||||
../../ldr/mach-o/h/architecture/byte_order.h \
|
||||
../../ldr/mach-o/h/arm/_types.h \
|
||||
../../ldr/mach-o/h/i386/_types.h \
|
||||
../../ldr/mach-o/h/i386/eflags.h \
|
||||
../../ldr/mach-o/h/libkern/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/i386/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/i386/_OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/machine/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/mach-o/arm/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/arm64/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/fat.h \
|
||||
../../ldr/mach-o/h/mach-o/hppa/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/i860/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/loader.h \
|
||||
../../ldr/mach-o/h/mach-o/m88k/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/nlist.h \
|
||||
../../ldr/mach-o/h/mach-o/ppc/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/sparc/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/stab.h \
|
||||
../../ldr/mach-o/h/mach-o/x86_64/reloc.h \
|
||||
../../ldr/mach-o/h/mach/arm/_structs.h \
|
||||
../../ldr/mach-o/h/mach/arm/boolean.h \
|
||||
../../ldr/mach-o/h/mach/arm/thread_state.h \
|
||||
../../ldr/mach-o/h/mach/arm/thread_status.h \
|
||||
../../ldr/mach-o/h/mach/arm/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/_structs.h \
|
||||
../../ldr/mach-o/h/mach/i386/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/fp_reg.h \
|
||||
../../ldr/mach-o/h/mach/i386/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/i386/thread_state.h \
|
||||
../../ldr/mach-o/h/mach/i386/thread_status.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/machine.h \
|
||||
../../ldr/mach-o/h/mach/machine/boolean.h \
|
||||
../../ldr/mach-o/h/mach/machine/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/machine/thread_status.h \
|
||||
../../ldr/mach-o/h/mach/machine/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/message.h \
|
||||
../../ldr/mach-o/h/mach/port.h \
|
||||
../../ldr/mach-o/h/mach/ppc/_structs.h \
|
||||
../../ldr/mach-o/h/mach/ppc/boolean.h \
|
||||
../../ldr/mach-o/h/mach/ppc/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/ppc/thread_status.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/shared_region.h \
|
||||
../../ldr/mach-o/h/mach/vm_prot.h \
|
||||
../../ldr/mach-o/h/mach/vm_types.h \
|
||||
../../ldr/mach-o/h/ppc/_types.h \
|
||||
../../ldr/mach-o/h/sys/_posix_availability.h \
|
||||
../../ldr/mach-o/h/sys/_symbol_aliasing.h \
|
||||
../../ldr/mach-o/h/sys/appleapiopts.h \
|
||||
../../ldr/mach-o/h/sys/cdefs.h \
|
||||
../../ldr/mach-o/h/sys/ptrace.h \
|
||||
../../ldr/mach-o/macho_node.h ../deb_pc.hpp ../debmod.h \
|
||||
../pc_debmod.h ../pc_regs.hpp mac_debmod.cpp \
|
||||
mac_debmod.h macbase_debmod.h symmacho.hpp
|
||||
$(F)mac_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 \
|
||||
../../ldr/mach-o/macho_node.h ../common_local_impl.cpp \
|
||||
../common_stub_impl.cpp ../dbg_rpc_client.h \
|
||||
../dbg_rpc_engine.h ../deb_pc.hpp ../debmod.h \
|
||||
../macho_rebase.cpp ../pc_local_impl.cpp ../pc_regs.hpp \
|
||||
../rpc_debmod.h mac_local_impl.cpp mac_stub.cpp \
|
||||
symmacho.hpp
|
||||
$(F)mac_user$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)dbg.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)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/mach-o/h/architecture/byte_order.h \
|
||||
../../ldr/mach-o/h/arm/_types.h \
|
||||
../../ldr/mach-o/h/i386/_types.h \
|
||||
../../ldr/mach-o/h/libkern/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/i386/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/i386/_OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/machine/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/mach-o/fat.h \
|
||||
../../ldr/mach-o/h/mach-o/nlist.h \
|
||||
../../ldr/mach-o/h/mach-o/reloc.h \
|
||||
../../ldr/mach-o/h/mach/arm/boolean.h \
|
||||
../../ldr/mach-o/h/mach/arm/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/machine.h \
|
||||
../../ldr/mach-o/h/mach/machine/boolean.h \
|
||||
../../ldr/mach-o/h/mach/machine/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/port.h \
|
||||
../../ldr/mach-o/h/mach/ppc/boolean.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/shared_region.h \
|
||||
../../ldr/mach-o/h/mach/vm_prot.h \
|
||||
../../ldr/mach-o/h/mach/vm_types.h \
|
||||
../../ldr/mach-o/h/ppc/_types.h \
|
||||
../../ldr/mach-o/h/sys/_posix_availability.h \
|
||||
../../ldr/mach-o/h/sys/_symbol_aliasing.h \
|
||||
../../ldr/mach-o/h/sys/appleapiopts.h \
|
||||
../../ldr/mach-o/h/sys/cdefs.h \
|
||||
../../ldr/mach-o/h/sys/ptrace.h \
|
||||
../../ldr/mach-o/macho_node.h ../common_local_impl.cpp \
|
||||
../common_stub_impl.cpp ../deb_pc.hpp ../debmod.h \
|
||||
../macho_rebase.cpp ../pc_debmod.h ../pc_local_impl.cpp \
|
||||
../pc_regs.hpp mac_debmod.h mac_local_impl.cpp \
|
||||
mac_user.cpp macbase_debmod.h symmacho.hpp
|
||||
$(F)macbase_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 \
|
||||
../../ldr/mach-o/h/architecture/byte_order.h \
|
||||
../../ldr/mach-o/h/arm/_types.h \
|
||||
../../ldr/mach-o/h/i386/_types.h \
|
||||
../../ldr/mach-o/h/libkern/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/i386/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/i386/_OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/machine/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/mach-o/fat.h \
|
||||
../../ldr/mach-o/h/mach/arm/boolean.h \
|
||||
../../ldr/mach-o/h/mach/arm/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/machine.h \
|
||||
../../ldr/mach-o/h/mach/machine/boolean.h \
|
||||
../../ldr/mach-o/h/mach/machine/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/ppc/boolean.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_types.h \
|
||||
../../ldr/mach-o/h/ppc/_types.h ../deb_pc.hpp \
|
||||
../debmod.h ../pc_debmod.h ../pc_regs.hpp \
|
||||
macbase_debmod.cpp macbase_debmod.h symmacho.hpp
|
||||
$(F)symmacho$(O): $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
|
||||
$(I)config.hpp $(I)diskio.hpp $(I)entry.hpp $(I)err.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 \
|
||||
$(IRRXML)CXMLReaderImpl.h $(IRRXML)fast_atof.h \
|
||||
$(IRRXML)heapsort.h $(IRRXML)irrArray.h \
|
||||
$(IRRXML)irrString.h $(IRRXML)irrTypes.h \
|
||||
$(IRRXML)irrXML.h ../../ldr/ar/aixar.hpp \
|
||||
../../ldr/ar/ar.hpp ../../ldr/ar/arcmn.cpp \
|
||||
../../ldr/mach-o/../ar/ar.hpp \
|
||||
../../ldr/mach-o/../idaldr.h ../../ldr/mach-o/base.cpp \
|
||||
../../ldr/mach-o/common.cpp ../../ldr/mach-o/common.h \
|
||||
../../ldr/mach-o/dsym.cpp ../../ldr/mach-o/dsym.h \
|
||||
../../ldr/mach-o/h/architecture/byte_order.h \
|
||||
../../ldr/mach-o/h/arm/_types.h \
|
||||
../../ldr/mach-o/h/i386/_types.h \
|
||||
../../ldr/mach-o/h/i386/eflags.h \
|
||||
../../ldr/mach-o/h/libkern/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/i386/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/i386/_OSByteOrder.h \
|
||||
../../ldr/mach-o/h/libkern/machine/OSByteOrder.h \
|
||||
../../ldr/mach-o/h/mach-o/arm/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/arm64/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/fat.h \
|
||||
../../ldr/mach-o/h/mach-o/hppa/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/i860/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/loader.h \
|
||||
../../ldr/mach-o/h/mach-o/m88k/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/nlist.h \
|
||||
../../ldr/mach-o/h/mach-o/ppc/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/sparc/reloc.h \
|
||||
../../ldr/mach-o/h/mach-o/stab.h \
|
||||
../../ldr/mach-o/h/mach-o/x86_64/reloc.h \
|
||||
../../ldr/mach-o/h/mach/arm/_structs.h \
|
||||
../../ldr/mach-o/h/mach/arm/boolean.h \
|
||||
../../ldr/mach-o/h/mach/arm/thread_state.h \
|
||||
../../ldr/mach-o/h/mach/arm/thread_status.h \
|
||||
../../ldr/mach-o/h/mach/arm/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/_structs.h \
|
||||
../../ldr/mach-o/h/mach/i386/boolean.h \
|
||||
../../ldr/mach-o/h/mach/i386/fp_reg.h \
|
||||
../../ldr/mach-o/h/mach/i386/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/i386/thread_state.h \
|
||||
../../ldr/mach-o/h/mach/i386/thread_status.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/i386/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/kmod.h \
|
||||
../../ldr/mach-o/h/mach/machine.h \
|
||||
../../ldr/mach-o/h/mach/machine/boolean.h \
|
||||
../../ldr/mach-o/h/mach/machine/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/machine/thread_status.h \
|
||||
../../ldr/mach-o/h/mach/machine/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/message.h \
|
||||
../../ldr/mach-o/h/mach/port.h \
|
||||
../../ldr/mach-o/h/mach/ppc/_structs.h \
|
||||
../../ldr/mach-o/h/mach/ppc/boolean.h \
|
||||
../../ldr/mach-o/h/mach/ppc/kern_return.h \
|
||||
../../ldr/mach-o/h/mach/ppc/thread_status.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_param.h \
|
||||
../../ldr/mach-o/h/mach/ppc/vm_types.h \
|
||||
../../ldr/mach-o/h/mach/vm_prot.h \
|
||||
../../ldr/mach-o/h/mach/vm_types.h \
|
||||
../../ldr/mach-o/h/ppc/_types.h \
|
||||
../../ldr/mach-o/h/sys/_posix_availability.h \
|
||||
../../ldr/mach-o/h/sys/_symbol_aliasing.h \
|
||||
../../ldr/mach-o/h/sys/cdefs.h \
|
||||
../../ldr/mach-o/macho_node.h ../debmod.h symmacho.cpp \
|
||||
symmacho.hpp
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,249 +0,0 @@
|
||||
#ifndef SYMMACHO_H
|
||||
#define SYMMACHO_H
|
||||
|
||||
// manage the mach-o images in a darwin process
|
||||
|
||||
#include <pro.h>
|
||||
#include <idd.hpp>
|
||||
#include <map>
|
||||
|
||||
class debmod_t;
|
||||
class linput_t;
|
||||
|
||||
typedef std::map<ea_t, qstring> strings_cache_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct macho_visitor_t
|
||||
{
|
||||
int flags;
|
||||
#define MV_UUID 0x0001 // visit uuid
|
||||
#define MV_FUNCTION_STARTS 0x0002 // visit function start eas
|
||||
#define MV_SYMBOLS 0x0004 // visit symbols
|
||||
#define MV_SEGMENTS 0x0008 // visit segments
|
||||
#define MV_SECTIONS 0x0010 // visit sections
|
||||
#define MV_PLATFORM_INFO 0x0020 // visit build version info
|
||||
|
||||
uint32 subtype; // mh.filetype
|
||||
asize_t size; // image size
|
||||
sval_t slide; // ASLR slide
|
||||
bytevec_t uuid; // filled if MV_UUID is set
|
||||
uint32 version; // platform version
|
||||
|
||||
macho_visitor_t(int _flags = 0)
|
||||
: flags(_flags), subtype(0), size(0), slide(0), version(0) {}
|
||||
|
||||
virtual void visit_function_start(ea_t /*ea*/) {}
|
||||
virtual void visit_symbol(ea_t /*ea*/, const char * /*name*/) {}
|
||||
virtual void visit_segment(ea_t /*start*/, ea_t /*end*/, const qstring & /*name*/, bool /*is_code*/) {}
|
||||
virtual void visit_section(ea_t /*start*/, ea_t /*end*/, const qstring & /*sect*/, const qstring & /*seg*/, bool /*is_code*/) {}
|
||||
|
||||
// called when function start info could not be found/loaded
|
||||
virtual void handle_function_start_error() {}
|
||||
// called just before a symbol is visited when cpu is CPU_TYPE_ARM
|
||||
virtual void handle_thumb(ea_t /*ea*/, const char * /*name*/, bool /*is_thumb*/) {}
|
||||
|
||||
DEFINE_VIRTUAL_DTOR(macho_visitor_t)
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class macho_utils_t
|
||||
{
|
||||
public:
|
||||
debmod_t *dbgmod;
|
||||
|
||||
int arch; // PLFM_386 or PLFM_ARM
|
||||
int addrsize; // size of an address in the target process
|
||||
bool is64; // is target process 64-bit?
|
||||
bool warned; // warned the user about using SYMBOL_PATH when remote debugging
|
||||
|
||||
// sometimes macho images might share a common string table. ensure the same string table isn't loaded twice.
|
||||
strings_cache_t strcache;
|
||||
|
||||
macho_utils_t(debmod_t *_dbgmod, int _arch);
|
||||
DEFINE_VIRTUAL_DTOR(macho_utils_t)
|
||||
|
||||
virtual void clear(void);
|
||||
|
||||
int get_cputype(void) const;
|
||||
void update_bitness(void);
|
||||
|
||||
size_t read_mem(ea_t ea, void *buf, size_t size);
|
||||
bool read(ea_t ea, void *buf, size_t size) { return read_mem(ea, buf, size) == size; }
|
||||
void get_ptr_value(ea_t *val, const uchar *buf) const;
|
||||
|
||||
bool is_exe_header(ea_t base);
|
||||
|
||||
virtual bool parse_macho_file(const char *path, ea_t base, macho_visitor_t &mv, const bytevec_t &uuid) const;
|
||||
virtual bool parse_macho_input(linput_t *li, ea_t base, macho_visitor_t &mv) const;
|
||||
virtual bool parse_macho_mem(ea_t base, macho_visitor_t &mv, uint32 hints = 0);
|
||||
|
||||
linput_t *create_mem_input(ea_t base);
|
||||
|
||||
bool calc_macho_uuid(bytevec_t *uuid, linput_t *li) const;
|
||||
bool match_macho_uuid(linput_t *li, const bytevec_t &uuid) const;
|
||||
|
||||
bool calc_image_info(uint32 *subtype, asize_t *size, bytevec_t *uuid, ea_t base);
|
||||
bool calc_image_info(uint32 *subtype, asize_t *size, bytevec_t *uuid, const char *path) const;
|
||||
bool calc_image_info(uint32 *subtype, asize_t *size, bytevec_t *uuid, linput_t *li, ea_t base) const;
|
||||
|
||||
static qstring expand_home_dir(const char *path);
|
||||
|
||||
static void merge(
|
||||
meminfo_vec_t &res,
|
||||
const meminfo_vec_t &low,
|
||||
const meminfo_vec_t &high);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct dyld_all_image_infos_t
|
||||
{
|
||||
uint32 version;
|
||||
uint32 num_info;
|
||||
ea_t info_array;
|
||||
ea_t dyld_notify;
|
||||
ea_t dyld_image_load_address;
|
||||
ea_t dyld_image_infos_address;
|
||||
ea_t shared_cache_slide;
|
||||
ea_t shared_cache_base_address;
|
||||
|
||||
dyld_all_image_infos_t() { clear(); }
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
enum dyld_image_mode_t
|
||||
{
|
||||
DYLD_IMAGE_ERROR = -1,
|
||||
DYLD_IMAGE_ADDING = 0,
|
||||
DYLD_IMAGE_REMOVING = 1,
|
||||
DYLD_IMAGE_INFO_CHANGE = 2,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct dll_visitor_t
|
||||
{
|
||||
virtual void visit_dll(
|
||||
ea_t base,
|
||||
asize_t size,
|
||||
const char *name,
|
||||
const bytevec_t &uuid) = 0;
|
||||
|
||||
DEFINE_VIRTUAL_DTOR(dll_visitor_t)
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct dyld_cache_visitor_t
|
||||
{
|
||||
int flags;
|
||||
#define DCV_MAPPINGS 0x1 // visit shared region mappings
|
||||
|
||||
dyld_cache_visitor_t(int _flags) : flags(_flags) {}
|
||||
|
||||
virtual void visit_mapping(ea_t /*start_ea*/, ea_t /*end_ea*/) {}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class dyld_utils_t : public macho_utils_t
|
||||
{
|
||||
typedef macho_utils_t inherited;
|
||||
|
||||
template<typename H> bool is_dyld_header(ea_t base, char *filename, size_t namesize, uint32 magic);
|
||||
|
||||
bool is_dyld_header_64(ea_t base, char *filename, size_t namesize);
|
||||
bool is_dyld_header_32(ea_t base, char *filename, size_t namesize);
|
||||
|
||||
public:
|
||||
ea_t base_ea; // base address of dyld ifself
|
||||
ea_t entry_ea; // dyld's entry point
|
||||
ea_t infos_ea; // address of _dyld_all_image_infos
|
||||
ea_t ranges_ea; // address of _dyld_shared_cache_ranges
|
||||
|
||||
dyld_all_image_infos_t infos;
|
||||
|
||||
rangeset_t shared_cache_ranges;
|
||||
|
||||
qstring symbol_path;
|
||||
|
||||
dyld_utils_t(debmod_t *_dbgmod, int _arch);
|
||||
DEFINE_VIRTUAL_DTOR(dyld_utils_t)
|
||||
|
||||
virtual void clear(void) override;
|
||||
|
||||
bool is_shared_cache_lib(ea_t base) const { return shared_cache_ranges.contains(base); }
|
||||
bool is_system_lib(ea_t base) const { return base == base_ea || is_shared_cache_lib(base); }
|
||||
|
||||
bool is_dyld_header(ea_t base, char *filename, size_t namesize);
|
||||
|
||||
bool update_infos(void);
|
||||
bool update_ranges(void);
|
||||
|
||||
virtual bool parse_macho_mem(ea_t base, macho_visitor_t &mv, uint32 hints = 0) override;
|
||||
|
||||
bool parse_info_array(uint32 count, ea_t info_array, dll_visitor_t &dv);
|
||||
bool parse_dyld_cache_header(dyld_cache_visitor_t &dcv);
|
||||
|
||||
bool untag(ea_t *ea) const;
|
||||
|
||||
bool get_symbol_file_path(qstring *path, const char *module) const;
|
||||
bool parse_local_symbol_file(
|
||||
ea_t base,
|
||||
const char *module,
|
||||
const bytevec_t &uuid,
|
||||
macho_visitor_t &mv);
|
||||
};
|
||||
|
||||
struct kext_info_t
|
||||
{
|
||||
uint64 off;
|
||||
bytevec_t uuid;
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(kext_info_t);
|
||||
typedef qvector<kext_info_t> kext_info_vec_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class kernel_utils_t : public macho_utils_t
|
||||
{
|
||||
typedef macho_utils_t inherited;
|
||||
|
||||
qstring kdk_path; // path to a Kernel Development Kit
|
||||
linput_t *kcache_li; // if a kernelcache is present in the KDK, we can use it to parse kexts
|
||||
uint64 prelink_data_off; // offset of __PRELINK_DATA, required for parsing prelinked kexts
|
||||
kext_info_vec_t prelinked_kexts; // associate each kext's UUID with its offset in the kernelcache
|
||||
|
||||
uint64 find_kext_offset(const bytevec_t &uuid) const;
|
||||
|
||||
public:
|
||||
kernel_utils_t(debmod_t *_dbgmod, int _arch)
|
||||
: inherited(_dbgmod, _arch), kcache_li(NULL), prelink_data_off(0) {}
|
||||
|
||||
virtual ~kernel_utils_t(void) { kernel_utils_t::clear(); }
|
||||
|
||||
virtual void clear(void) override;
|
||||
|
||||
void set_kdk_path(const qstring &path);
|
||||
|
||||
// detect if a kernelcache is present and collect the prelinked kext info
|
||||
bool parse_kcache(const bytevec_t &kernel_uuid);
|
||||
|
||||
// apply the given visitor to a matching kext in the kernelcache
|
||||
bool parse_prelinked_kext(macho_visitor_t &mv, ea_t base, const bytevec_t &uuid);
|
||||
|
||||
#define KDK_SEARCH_DEFAULT 0x0 // look for any matching binary
|
||||
#define KDK_SEARCH_DSYM 0x1 // look for a binary with a companion dSYM
|
||||
#define KDK_SEARCH_KCACHE 0x2 // look for a kernelcache
|
||||
|
||||
// get the path to a matching binary in the KDK
|
||||
bool find_kdk_file(
|
||||
qstring *path,
|
||||
int cpu_subtype,
|
||||
const bytevec_t &uuid,
|
||||
const char *name,
|
||||
uint32 flags = KDK_SEARCH_DEFAULT) const;
|
||||
|
||||
// check if the given kext appears in the KDK, either as a standalone binary
|
||||
// or as a prelinked kext in a kernelcache
|
||||
bool find_kext(const bytevec_t &uuid, const char *kext_name) const;
|
||||
};
|
||||
|
||||
#endif // SYMMACHO_H
|
||||
@@ -1,197 +0,0 @@
|
||||
|
||||
#include "../ldr/mach-o/macho_node.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool rebase_scattered_segments(ea_t base)
|
||||
{
|
||||
netnode node;
|
||||
node.create(MACHO_NODE);
|
||||
|
||||
// detect if the code and data segments have moved relative to each other.
|
||||
// if so, we cannot simply apply a uniform delta to the entire database.
|
||||
// we must rebase one segment at a time.
|
||||
ea_t ldr_data = 0;
|
||||
ea_t ldr_text = 0;
|
||||
|
||||
if ( node.hashval("__DATA", &ldr_data, sizeof(ldr_data), SEGM_TAG) == -1
|
||||
|| node.hashval("__TEXT", &ldr_text, sizeof(ldr_text), SEGM_TAG) == -1 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ea_t dbg_data = g_dbgmod.dbg_get_segm_start(base, "__DATA");
|
||||
ea_t dbg_text = g_dbgmod.dbg_get_segm_start(base, "__TEXT");
|
||||
|
||||
if ( dbg_data == BADADDR || dbg_text == BADADDR )
|
||||
return false;
|
||||
|
||||
adiff_t slide = (dbg_data - dbg_text) - (ldr_data - ldr_text);
|
||||
|
||||
if ( slide == 0 )
|
||||
return false;
|
||||
|
||||
scattered_image_t si;
|
||||
// we have detected segment scattering.
|
||||
// ensure that we can collect the vmaddr for each loader segment in IDA.
|
||||
if ( g_dbgmod.dbg_get_scattered_image(si, base) <= 0 )
|
||||
return false;
|
||||
|
||||
uint8 ubuf[16];
|
||||
bytevec_t uuid;
|
||||
// it is quite possible that the input file does not match the image in the cache.
|
||||
// if there is a mismatch we warn the user.
|
||||
if ( !g_dbgmod.dbg_get_image_uuid(&uuid, base)
|
||||
|| uuid.size() != sizeof(ubuf)
|
||||
|| node.supval(MACHO_ALT_UUID, ubuf, sizeof(ubuf)) <= 0
|
||||
|| memcmp(uuid.begin(), ubuf, sizeof(ubuf)) != 0 )
|
||||
{
|
||||
warning("AUTOHIDE DATABASE\n"
|
||||
"UUID mismatch between the input file and the image in memory.\n"
|
||||
"\n"
|
||||
"This could mean your dyld_shared_cache is out of sync with the library on disk,\n"
|
||||
"and the database will likely not be rebased properly.\n"
|
||||
"\n"
|
||||
"To ensure proper rebasing, please confirm that the input file was included\n"
|
||||
"the last time update_dyld_shared_cache was run.\n");
|
||||
}
|
||||
|
||||
// adjust any pointers between the code and data segments
|
||||
show_wait_box("Rebasing CODE -> DATA pointers");
|
||||
// we want to patch pointers in the database without writing to debugger memory
|
||||
lock_dbgmem_config();
|
||||
|
||||
for ( nodeidx_t nidx = node.charfirst(CODE_TAG);
|
||||
nidx != BADNODE && !user_cancelled();
|
||||
nidx = node.charnext(nidx, CODE_TAG) )
|
||||
{
|
||||
ea_t ea = node2ea(nidx);
|
||||
uchar kind = node.charval_ea(ea, CODE_TAG);
|
||||
switch ( kind )
|
||||
{
|
||||
case 1: // 32-bit pointer
|
||||
add_dword(ea, slide);
|
||||
break;
|
||||
case 2: // 64-bit pointer
|
||||
add_qword(ea, slide);
|
||||
break;
|
||||
default: // TODO: there are many more. we will deal with them later
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
replace_wait_box("Rebasing DATA -> CODE pointers");
|
||||
|
||||
for ( nodeidx_t nidx = node.charfirst(DATA_TAG);
|
||||
nidx != BADNODE && !user_cancelled();
|
||||
nidx = node.charnext(nidx, DATA_TAG) )
|
||||
{
|
||||
ea_t ea = node2ea(nidx);
|
||||
uchar kind = node.charval_ea(ea, DATA_TAG);
|
||||
switch ( kind )
|
||||
{
|
||||
case 1: // pointer
|
||||
if ( inf_is_64bit() )
|
||||
add_qword(ea, -slide);
|
||||
else
|
||||
add_dword(ea, -slide);
|
||||
break;
|
||||
default: // TODO: there a few more. we will deal with them later
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hide_wait_box();
|
||||
unlock_dbgmem_config();
|
||||
|
||||
qvector<segment_t *> ldrsegs;
|
||||
// we must collect all the loader segments before we start calling move_segm().
|
||||
// this is to avoid altering the areacb_t as we're iterating over it.
|
||||
for ( segment_t *s=get_first_seg(); s != NULL; s=get_next_seg(s->start_ea) )
|
||||
{
|
||||
if ( s->is_loader_segm() )
|
||||
ldrsegs.push_back(s);
|
||||
}
|
||||
|
||||
show_wait_box("Rebasing scattered segments");
|
||||
|
||||
bool ok = true;
|
||||
size_t ls_count = ldrsegs.size();
|
||||
size_t ss_count = si.size();
|
||||
|
||||
// rebase each loader segment according to its matching segment in the scattered image.
|
||||
// currently we require the list of scattered segments and the list of loader segments
|
||||
// to have the exact same ordering. this is because we have no way to uniquely match a
|
||||
// loader segment and a scattered segment without some context. the segment's name, start_ea,
|
||||
// type, and selector are all not sufficient in this situation.
|
||||
for ( size_t i = 0; i < ls_count && !user_cancelled(); i++ )
|
||||
{
|
||||
segment_t *s = ldrsegs[i];
|
||||
|
||||
qstring name;
|
||||
get_segm_name(&name, s);
|
||||
ea_t rebase_to = 0;
|
||||
|
||||
if ( i < ss_count && name == si[i].name )
|
||||
{
|
||||
// found the loader segment in memory. rebase it to this address.
|
||||
rebase_to = si[i].start_ea;
|
||||
}
|
||||
else if ( name == "UNDEF" )
|
||||
{
|
||||
// UNDEF segment is not actually in memory. just rebase it along with the other data segments.
|
||||
rebase_to = s->start_ea + (dbg_data - ldr_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg("%a: Failed to find segment %s in process memory!\n", s->start_ea, name.c_str());
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( s->start_ea != rebase_to )
|
||||
{
|
||||
replace_wait_box("Moving segment %s to %#a", name.c_str(), rebase_to);
|
||||
int code = move_segm(s, rebase_to, MSF_PRIORITY|MSF_SILENT);
|
||||
if ( code != MOVE_SEGM_OK )
|
||||
{
|
||||
msg("%a: Failed to rebase segment %s to %a, code=%d\n", s->start_ea, name.c_str(), rebase_to, code);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hide_wait_box();
|
||||
set_imagebase(base);
|
||||
node.altset(MACHO_ALT_IMAGEBASE, base);
|
||||
|
||||
// update segm eas in the database
|
||||
qstring idx;
|
||||
for ( ssize_t s = node.hashfirst(&idx, SEGM_TAG);
|
||||
s >= 0;
|
||||
s = node.hashnext(&idx, idx.c_str(), SEGM_TAG) )
|
||||
{
|
||||
ea_t start = g_dbgmod.dbg_get_segm_start(base, idx.c_str());
|
||||
if ( start != BADADDR )
|
||||
node.hashset(idx.c_str(), &start, sizeof(start), SEGM_TAG);
|
||||
}
|
||||
|
||||
if ( !ok )
|
||||
{
|
||||
char buf[QMAXPATH];
|
||||
dbg_get_input_path(buf, sizeof(buf));
|
||||
warning("AUTOHIDE DATABASE\n"
|
||||
"Some loader segments were not rebased properly.\n"
|
||||
"\n"
|
||||
"This error might occur if you are debugging a dylib from /System/Library or /usr/lib,\n"
|
||||
"since these files have different segment info when they are loaded from dyld_shared_cache.\n"
|
||||
"\n"
|
||||
"To ensure more accurate segment info when debugging, you can:\n"
|
||||
"\n"
|
||||
" 1. open the dyld_shared_cache in IDA (usually found in /var/db/dyld/)\n"
|
||||
" 2. select the 'Apple DYLD cache (single module)' option\n"
|
||||
" 3. select %s from the list of modules\n"
|
||||
" 4. use %s as the input file path in the Process Options dialog\n", qbasename(buf), buf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
#ifdef __NT__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <pro.h>
|
||||
#include <ua.hpp>
|
||||
#include "pc_debmod.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//lint -esym(1566,pc_debmod_t::hwbpt_ea,pc_debmod_t::hwbpt_type)
|
||||
//lint -esym(1566,pc_debmod_t::dr6,pc_debmod_t::dr7)
|
||||
pc_debmod_t::pc_debmod_t()
|
||||
{
|
||||
static const uchar bpt[] = X86_BPT_CODE;
|
||||
bpt_code.append(bpt, sizeof(bpt));
|
||||
sp_idx = R_ESP;
|
||||
pc_idx = R_EIP;
|
||||
sr_idx = R_EFLAGS;
|
||||
fs_idx = R_FS;
|
||||
gs_idx = R_GS;
|
||||
cs_idx = R_CS;
|
||||
ds_idx = R_DS;
|
||||
es_idx = R_ES;
|
||||
ss_idx = R_SS;
|
||||
nregs = X86_NREGS;
|
||||
|
||||
cleanup_hwbpts();
|
||||
set_platform(get_local_platform());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int pc_debmod_t::get_regidx(const char *regname, int *clsmask)
|
||||
{
|
||||
return x86_get_regidx(clsmask, regname);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi pc_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t ea, int /* len */)
|
||||
{
|
||||
if ( type == BPT_SOFT )
|
||||
return BPT_OK;
|
||||
|
||||
return find_hwbpt_slot(ea, type) == -1 ? BPT_TOO_MANY : BPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// returns -1 if something is wrong
|
||||
int pc_debmod_t::find_hwbpt_slot(ea_t ea, bpttype_t type) const
|
||||
{
|
||||
for ( int i=0; i < MAX_BPT; i++ )
|
||||
{
|
||||
if ( hwbpt_ea[i] == ea && hwbpt_type[i] == type ) // another breakpoint is here
|
||||
return -1;
|
||||
if ( hwbpt_ea[i] == BADADDR ) // empty slot found
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool pc_debmod_t::add_hwbpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
int i = find_hwbpt_slot(ea, type); // get slot number
|
||||
if ( i != -1 )
|
||||
{
|
||||
hwbpt_ea[i] = ea;
|
||||
hwbpt_type[i] = type;
|
||||
if ( type == BPT_EXEC )
|
||||
type = 0; // exec bpts are encoded with 0 on x86
|
||||
|
||||
// length code used by the processor
|
||||
int lenc = (len == 2) ? 1
|
||||
: (len == 4) ? 3
|
||||
: (len == 8) ? 2
|
||||
: 0;
|
||||
|
||||
dr7 |= (1 << (i*2)); // enable local breakpoint
|
||||
dr7 |= (type << (16+(i*4))); // set breakpoint type
|
||||
dr7 |= (lenc << (18+(i*4))); // set breakpoint length
|
||||
|
||||
return refresh_hwbpts();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool pc_debmod_t::del_hwbpt(ea_t ea, bpttype_t type)
|
||||
{
|
||||
for ( int i=0; i < MAX_BPT; i++ )
|
||||
{
|
||||
if ( hwbpt_ea[i] == ea && hwbpt_type[i] == type )
|
||||
{
|
||||
hwbpt_ea[i] = BADADDR; // clean the address
|
||||
dr7 &= ~(3 << (i*2)); // clean the enable bits
|
||||
dr7 &= ~(0xF << (i*4+16)); // clean the length and type
|
||||
return refresh_hwbpts();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __NT__
|
||||
//--------------------------------------------------------------------------
|
||||
// Set hardware breakpoint for one thread
|
||||
bool pc_debmod_t::set_hwbpts(HANDLE hThread)
|
||||
{
|
||||
// sure_suspend_thread(ti);
|
||||
CONTEXT Context;
|
||||
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
|
||||
|
||||
BOOL ok = GetThreadContext(hThread, &Context);
|
||||
if ( !ok )
|
||||
{
|
||||
deberr("GetThreadContext");
|
||||
return false;
|
||||
}
|
||||
Context.Dr0 = hwbpt_ea[0];
|
||||
Context.Dr1 = hwbpt_ea[1];
|
||||
Context.Dr2 = hwbpt_ea[2];
|
||||
Context.Dr3 = hwbpt_ea[3];
|
||||
Context.Dr6 = 0;
|
||||
Context.Dr7 = dr7;
|
||||
|
||||
ok = SetThreadContext(hThread, &Context);
|
||||
if ( !ok )
|
||||
{
|
||||
deberr("SetThreadContext");
|
||||
}
|
||||
// sure_resume_thread(ti);
|
||||
return ok != FALSE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ea_t pc_debmod_t::is_hwbpt_triggered(thid_t id, bool is_stepping)
|
||||
{
|
||||
CONTEXT Context;
|
||||
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
|
||||
HANDLE h = get_thread_handle(id);
|
||||
if ( GetThreadContext(h, &Context) )
|
||||
{
|
||||
for ( int i=0; i < MAX_BPT; i++ )
|
||||
{
|
||||
if ( (Context.Dr7 & uint32(1 << (i*2)))
|
||||
&& (Context.Dr6 & uint32(1 << i)) ) // Local hardware breakpoint 'i'
|
||||
{
|
||||
ULONG_PTR *dr = NULL;
|
||||
switch ( i )
|
||||
{
|
||||
case 0: dr = &Context.Dr0; break;
|
||||
case 1: dr = &Context.Dr1; break;
|
||||
case 2: dr = &Context.Dr2; break;
|
||||
case 3: dr = &Context.Dr3; break;
|
||||
}
|
||||
if ( dr == NULL )
|
||||
break;
|
||||
if ( hwbpt_ea[i] == *dr )
|
||||
{
|
||||
set_hwbpts(h); // Clear the status bits
|
||||
// do not report exec breakpoint if it occurs while we are stepping
|
||||
if ( is_stepping && hwbpt_type[i] == BPT_EXEC )
|
||||
break;
|
||||
return hwbpt_ea[i];
|
||||
}
|
||||
//? TRACING else
|
||||
// debdeb("System hardware breakpoint at %08X ???\n", *dr); //?
|
||||
// what to do ?:
|
||||
// reset it, and continue as if no event were received ?
|
||||
// send it to IDA, and let the user setup a "stop on non-debugger hardware breakpoint" option ?
|
||||
}
|
||||
}
|
||||
}
|
||||
return BADADDR;
|
||||
}
|
||||
#endif // ifdef __NT__
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void pc_debmod_t::cleanup_hwbpts()
|
||||
{
|
||||
for ( int i=0; i < MAX_BPT; i++ )
|
||||
{
|
||||
hwbpt_ea[i] = BADADDR;
|
||||
hwbpt_type[i] = bpttype_t(0);
|
||||
}
|
||||
dr6 = 0;
|
||||
dr7 = 0x100; // exact local breakpoints
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ea_t pc_debmod_t::calc_appcall_stack(const regvals_t ®vals)
|
||||
{
|
||||
ea_t ea = inherited::calc_appcall_stack(regvals);
|
||||
#ifndef __X86__
|
||||
// do not touch the red zone (used by gcc)
|
||||
ea = ea > 128 ? ea - 128 : BADADDR;
|
||||
#endif
|
||||
return ea;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int pc_debmod_t::finalize_appcall_stack(
|
||||
call_context_t &,
|
||||
regval_map_t &,
|
||||
bytevec_t &stk)
|
||||
{
|
||||
// pc-specific: add endless loop, so user does not execute unwanted code
|
||||
// after manual appcall. we do not really need to write bpt,
|
||||
// but it is easy to include it here than skip it.
|
||||
// previously we reserved 'addrsize' bytes on the stack for this purpose,
|
||||
// and we use 3 of them.
|
||||
static const uchar bpt_and_loop[] = { 0xCC, 0xEB, 0xFE };
|
||||
stk.append(bpt_and_loop, sizeof(bpt_and_loop));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool pc_debmod_t::should_stop_appcall(
|
||||
thid_t tid,
|
||||
const debug_event_t *event,
|
||||
ea_t ea)
|
||||
{
|
||||
if ( inherited::should_stop_appcall(tid, event, ea) )
|
||||
return true;
|
||||
|
||||
// Check if the current instruction is a "RET" and then dereferences
|
||||
// the contents of SP to find the return address. IF it matches, it is
|
||||
// time to stop
|
||||
regvals_t regs;
|
||||
regs.resize(X86_NREGS);
|
||||
do
|
||||
{
|
||||
// Start by reading registers
|
||||
if ( dbg_read_registers(tid, X86_RC_GENERAL, regs.begin(), NULL) != DRC_OK )
|
||||
break;
|
||||
|
||||
// Get the opcodes
|
||||
uchar opcode;
|
||||
if ( dbg_read_memory((ea_t)regs[pc_idx].ival, &opcode, 1, NULL) != 1 )
|
||||
break;
|
||||
// Check for "RET" and "RET n"
|
||||
if ( opcode != 0xC3 && opcode != 0xC2 )
|
||||
break;
|
||||
|
||||
// Dereference value at ESP
|
||||
ea_t at_sp = BADADDR;
|
||||
if ( dbg_read_memory((ea_t)regs[sp_idx].ival, &at_sp, sizeof(at_sp), NULL) != sizeof(at_sp) )
|
||||
break;
|
||||
return ea == at_sp; // time to stop!
|
||||
} while ( false );
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool pc_debmod_t::preprocess_appcall_cleanup(thid_t, call_context_t &ctx)
|
||||
{
|
||||
// Linux 2.6.24-19 has a bug(?):
|
||||
// it doesn't clear trace flag after single-stepping
|
||||
// so if we single-step and then make an appcall, we would restore eflags with TF set
|
||||
// but next time we resume the program, kernel thinks that TF was set by the user
|
||||
// and doesn't clear it, and so our appcall stops immediately
|
||||
// to prevent that, we'll always clear trace flag before restoring eflags
|
||||
if ( ctx.saved_regs.size() > sr_idx )
|
||||
ctx.saved_regs[sr_idx].ival &= ~0x100;
|
||||
return true; // success
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void pc_debmod_t::read_fpu_registers(
|
||||
regval_t *values,
|
||||
int clsmask,
|
||||
const void *fptr,
|
||||
size_t step)
|
||||
{
|
||||
const uchar *vptr = (const uchar *)fptr;
|
||||
for ( int i=0; i < 8; i++,vptr+=step )
|
||||
{
|
||||
if ( (clsmask & X86_RC_FPU) != 0 )
|
||||
{
|
||||
regval_t *v = &values[R_ST0+i];
|
||||
memcpy(v->fval, vptr, 10); //-V512 v->fval underflow
|
||||
v->rvtype = RVT_FLOAT;
|
||||
}
|
||||
if ( (clsmask & X86_RC_MMX) != 0 )
|
||||
values[R_MMX0+i].set_bytes(vptr, 8);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
const char *pc_debmod_t::get_local_platform()
|
||||
{
|
||||
#ifdef __NT__
|
||||
# define LOCAL_PLATFORM "win32"
|
||||
#else
|
||||
# ifdef __MAC__
|
||||
# define LOCAL_PLATFORM "macosx"
|
||||
# else
|
||||
# ifdef __LINUX__
|
||||
# define LOCAL_PLATFORM "linux"
|
||||
# else
|
||||
# define LOCAL_PLATFORM "PC_UNDEFINED"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
return LOCAL_PLATFORM;
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
// 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
|
||||
memset(values[i].fval, 0, sizeof(values[i].fval));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 == 1 )
|
||||
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)
|
||||
{
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
#ifndef __RPC_DEBUGGER_MODULE__
|
||||
#define __RPC_DEBUGGER_MODULE__
|
||||
|
||||
#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
|
||||
@@ -1,224 +0,0 @@
|
||||
/*
|
||||
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 <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-2020\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();
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#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__)
|
||||
# define DEBUGGER_ID DEBUGGER_ID_X86_IA32_MACOSX_USER
|
||||
# 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
|
||||
@@ -1,174 +0,0 @@
|
||||
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
|
||||
MODULES += $(STUB)
|
||||
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
|
||||
@@ -1,256 +0,0 @@
|
||||
#include <set>
|
||||
#include <pro.h>
|
||||
#include <name.hpp>
|
||||
#include <kernwin.hpp>
|
||||
#include <dbg.hpp>
|
||||
#include <loader.hpp>
|
||||
#include "w32sehch.h"
|
||||
|
||||
static int req_id = -1;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// non-modal exception handler chooser
|
||||
struct x86seh_chooser_t : public chooser_t
|
||||
{
|
||||
protected:
|
||||
thid_t tid;
|
||||
qvector<uint32> list; //lint !e958 padding is required to align members
|
||||
qstring title_;
|
||||
|
||||
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,
|
||||
// `item_data` is a pointer to a 32-bit address
|
||||
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;
|
||||
|
||||
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
|
||||
};
|
||||
const char *const x86seh_chooser_t::header_[] =
|
||||
{
|
||||
"Address", // 0
|
||||
"Name", // 1
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
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_),
|
||||
list()
|
||||
{
|
||||
title_.sprnt("[%04X] - Structured exception handlers list", tid);
|
||||
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];
|
||||
|
||||
qstrvec_t &cols = *cols_;
|
||||
cols[0].sprnt("%08X", addr);
|
||||
get_nice_colored_name(&cols[1], addr, GNCN_NOCOLOR | GNCN_NOLABEL);
|
||||
CASSERT(qnumber(header_) == 2);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
chooser_t::cbret_t idaapi x86seh_chooser_t::enter(size_t n)
|
||||
{
|
||||
// assert: n < list.size()
|
||||
ea_t ea = ea_t(list[n]);
|
||||
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;
|
||||
|
||||
// `item_data` is a pointer to a 32-bit address
|
||||
uint32 item_addr = *(const uint32 *)item_data;
|
||||
if ( item_addr == uint32(-1) )
|
||||
return 0; // first item by default
|
||||
|
||||
// find `item_script` in the list
|
||||
const uint32 *p = list.find(item_addr);
|
||||
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 p_prev;
|
||||
uint32 p_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(rec.p_handler);
|
||||
excr_ea = rec.p_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]; // 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;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
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 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);
|
||||
return ch->choose() == 0;
|
||||
} //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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,428 +0,0 @@
|
||||
#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_CLSMASK 0x0007 // valid register classes in CONTEXT structure
|
||||
// we use X86_RC.. constants here
|
||||
#define THR_TRACING 0x0100 // expecting a STEP event
|
||||
#define THR_FSSAVED 0x0200 // remembered FS value
|
||||
#define THR_WOW64 0x0400 // is wow64 process?
|
||||
#define THR_NEWNAME 0x0800 // thread was renamed
|
||||
context_holder_t ctx_holder;
|
||||
ea_t callgate_ea;
|
||||
qstring name;
|
||||
|
||||
void invalidate_context(void) { flags &= ~THR_CLSMASK; if ( ctx_holder.ptr != NULL ) get_ctx().ContextFlags = 0; }
|
||||
bool read_context(int clsmask);
|
||||
bool write_context(int clsmask);
|
||||
void cvt_from_wow64(
|
||||
const WOW64_CONTEXT &wow64ctx,
|
||||
int clsmask,
|
||||
PCONTEXT xstate_ctx) const;
|
||||
void cvt_to_wow64(
|
||||
WOW64_CONTEXT *wow64ctx,
|
||||
PCONTEXT xstate_ctx,
|
||||
int clsmask) const;
|
||||
bool toggle_tbit(bool set_tbit);
|
||||
bool is_tracing(void) const { return (flags & THR_TRACING) != 0; }
|
||||
void set_tracing(void) { flags |= THR_TRACING; }
|
||||
void clr_tracing(void) { flags &= ~THR_TRACING; }
|
||||
#ifdef __X86__
|
||||
ea_t get_ip(void) { return read_context(X86_RC_GENERAL) ? get_ctx().Eip : BADADDR; }
|
||||
#else
|
||||
ea_t get_ip(void) { return read_context(X86_RC_GENERAL) ? get_ctx().Rip : BADADDR; }
|
||||
#endif
|
||||
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; }
|
||||
CONTEXT &get_ctx() const { QASSERT(1669, ctx_holder.ptr != NULL); return *ctx_holder.ptr; }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
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
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class win32_debmod_t : public winbase_debmod_t
|
||||
{
|
||||
typedef winbase_debmod_t inherited;
|
||||
|
||||
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 patch_context_struct(
|
||||
CONTEXT &ctx,
|
||||
int reg_idx,
|
||||
const thread_info_t *ti,
|
||||
const regval_t *value) const;
|
||||
|
||||
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(); }
|
||||
|
||||
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 invalidate_all_contexts(void);
|
||||
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
|
||||
|
||||
@@ -1,619 +0,0 @@
|
||||
#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)
|
||||
: is_wow64(WOW64_NONE),
|
||||
process_handle(INVALID_HANDLE_VALUE),
|
||||
dep_policy(dp_always_off),
|
||||
highdlls()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// 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;
|
||||
@@ -1,179 +0,0 @@
|
||||
#ifndef __WINBASE_HPP__
|
||||
#define __WINBASE_HPP__
|
||||
|
||||
// Base class for win32 and windbg modules
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
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; // use check_wow64_process()
|
||||
|
||||
protected:
|
||||
HANDLE process_handle;
|
||||
dep_policy_t dep_policy;
|
||||
highdll_vec_t highdlls;
|
||||
// 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;
|
||||
|
||||
public:
|
||||
virtual void idaapi dbg_term(void) override;
|
||||
|
||||
static win_tool_help_t *get_tool_help();
|
||||
static win_version_t winver;
|
||||
|
||||
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
|
||||
@@ -1,125 +0,0 @@
|
||||
#############################################################################
|
||||
# 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
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9
|
||||
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
|
||||
QTVER?=5.6.3-x64
|
||||
|
||||
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 $($*)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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
|
||||
@@ -1,543 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,621 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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;
|
||||
DEFINE_VIRTUAL_DTOR(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;
|
||||
DEFINE_VIRTUAL_DTOR(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
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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
|
||||
@@ -1,371 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,512 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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_ 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)
|
||||
//@}
|
||||
|
||||
/// \defgroup fh_options Tuning options
|
||||
//@{
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// \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 AArch64's custom fixup BRANCH26 uses size = 4,
|
||||
/// and width = 28, and shift = 2, and property FHF_VERIFY.
|
||||
|
||||
/// \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.
|
||||
|
||||
|
||||
/// 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);
|
||||
|
||||
|
||||
#endif // FIXUP_HPP
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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
|
||||
@@ -1,577 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 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]
|
||||
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
|
||||
//@{
|
||||
|
||||
/// 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; ///< canonical register name (case-insensitive)
|
||||
char *user; ///< user-defined register name
|
||||
char *cmt; ///< comment to appear near definition
|
||||
};
|
||||
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
|
||||
@@ -1,950 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FUNCS_HPP
|
||||
#define FUNCS_HPP
|
||||
#include <range.hpp>
|
||||
#include <bytes.hpp>
|
||||
|
||||
/*! \file funcs.hpp
|
||||
|
||||
\brief Routines for working with functions within the disassembled program.
|
||||
|
||||
This file also contains routines for working with library signatures
|
||||
(e.g. FLIRT).
|
||||
|
||||
Each function consists of function chunks. At least one function chunk
|
||||
must be present in the function definition - the function entry chunk.
|
||||
Other chunks are called function tails. There may be several of them
|
||||
for a function.
|
||||
|
||||
A function tail is a continuous range of addresses.
|
||||
It can be used in the definition of one or more functions.
|
||||
One function using the tail is singled out and called the tail owner.
|
||||
This function is considered as 'possessing' the tail.
|
||||
get_func() on a tail address will return the function possessing the tail.
|
||||
You can enumerate the functions using the tail by using ::func_parent_iterator_t.
|
||||
|
||||
Each function chunk in the disassembly is represented as an "range" (a range
|
||||
of addresses, see range.hpp for details) with characteristics.
|
||||
|
||||
A function entry must start with an instruction (code) byte.
|
||||
*/
|
||||
|
||||
struct stkpnt_t; // #include <frame.hpp>
|
||||
struct regvar_t; // #include <frame.hpp>
|
||||
struct llabel_t; // #include <frame.hpp>
|
||||
class insn_t; // #include <ua.hpp>
|
||||
|
||||
/// Register argument description.
|
||||
/// regargs are destroyed when the full function type is determined.
|
||||
struct regarg_t
|
||||
{
|
||||
int reg;
|
||||
type_t *type;
|
||||
char *name;
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/// A function is a set of continuous ranges of addresses with characteristics
|
||||
class func_t : public range_t
|
||||
{
|
||||
public:
|
||||
uint64 flags; ///< \ref FUNC_
|
||||
/// \defgroup FUNC_ Function flags
|
||||
/// Used by func_t::flags
|
||||
//@{
|
||||
#define FUNC_NORET 0x00000001 ///< Function doesn't return
|
||||
#define FUNC_FAR 0x00000002 ///< Far function
|
||||
#define FUNC_LIB 0x00000004 ///< Library function
|
||||
|
||||
#define FUNC_STATICDEF 0x00000008 ///< Static function
|
||||
|
||||
#define FUNC_FRAME 0x00000010 ///< Function uses frame pointer (BP)
|
||||
#define FUNC_USERFAR 0x00000020 ///< User has specified far-ness
|
||||
///< of the function
|
||||
#define FUNC_HIDDEN 0x00000040 ///< A hidden function chunk
|
||||
#define FUNC_THUNK 0x00000080 ///< Thunk (jump) function
|
||||
#define FUNC_BOTTOMBP 0x00000100 ///< BP points to the bottom of the stack frame
|
||||
#define FUNC_NORET_PENDING 0x00200 ///< Function 'non-return' analysis must be performed.
|
||||
///< This flag is verified upon func_does_return()
|
||||
#define FUNC_SP_READY 0x00000400 ///< SP-analysis has been performed.
|
||||
///< If this flag is on, the stack
|
||||
///< change points should not be not
|
||||
///< modified anymore. Currently this
|
||||
///< analysis is performed only for PC
|
||||
#define FUNC_FUZZY_SP 0x00000800 ///< Function changes SP in untraceable way,
|
||||
///< for example: and esp, 0FFFFFFF0h
|
||||
#define FUNC_PROLOG_OK 0x00001000 ///< Prolog analysis has be performed
|
||||
///< by last SP-analysis
|
||||
#define FUNC_PURGED_OK 0x00004000 ///< 'argsize' field has been validated.
|
||||
///< If this bit is clear and 'argsize'
|
||||
///< is 0, then we do not known the real
|
||||
///< number of bytes removed from
|
||||
///< the stack. This bit is handled
|
||||
///< by the processor module.
|
||||
#define FUNC_TAIL 0x00008000 ///< This is a function tail.
|
||||
///< Other bits must be clear
|
||||
///< (except #FUNC_HIDDEN).
|
||||
#define FUNC_LUMINA 0x00010000 ///< Function info is provided by Lumina.
|
||||
//@}
|
||||
|
||||
/// Is a far function?
|
||||
bool is_far(void) const { return (flags & FUNC_FAR) != 0; }
|
||||
/// Does function return?
|
||||
bool does_return(void) const { return (flags & FUNC_NORET) == 0; }
|
||||
/// Has SP-analysis been performed?
|
||||
bool analyzed_sp(void) const { return (flags & FUNC_SP_READY) != 0; }
|
||||
/// Needs prolog analysis?
|
||||
bool need_prolog_analysis(void) const { return (flags & FUNC_PROLOG_OK) == 0; }
|
||||
#ifndef SWIG
|
||||
union
|
||||
{
|
||||
/// attributes of a function entry chunk
|
||||
struct
|
||||
{
|
||||
#endif // SWIG
|
||||
//
|
||||
// Stack frame of the function. It is represented as a structure:
|
||||
//
|
||||
// +------------------------------------------------+
|
||||
// | 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
|
||||
//
|
||||
uval_t frame; ///< netnode id of frame structure - see frame.hpp
|
||||
asize_t frsize; ///< size of local variables part of frame in bytes.
|
||||
///< If #FUNC_FRAME is set and #fpd==0, the frame pointer
|
||||
///< (EBP) is assumed to point to the top of the local
|
||||
///< variables range.
|
||||
ushort frregs; ///< size of saved registers in frame. This range is
|
||||
///< immediately above the local variables range.
|
||||
asize_t argsize; ///< number of bytes purged from the stack
|
||||
///< upon returning
|
||||
asize_t fpd; ///< frame pointer delta. (usually 0, i.e. realBP==typicalBP)
|
||||
///< use update_fpd() to modify it.
|
||||
|
||||
bgcolor_t color; ///< user defined function color
|
||||
|
||||
// the following fields should not be accessed directly:
|
||||
|
||||
uint32 pntqty; ///< number of SP change points
|
||||
stkpnt_t *points; ///< array of SP change points.
|
||||
///< use ...stkpnt...() functions to access this array.
|
||||
|
||||
int regvarqty; ///< number of register variables (-1-not read in yet)
|
||||
///< use find_regvar() to read register variables
|
||||
regvar_t *regvars; ///< array of register variables.
|
||||
///< this array is sorted by: start_ea.
|
||||
///< use ...regvar...() functions to access this array.
|
||||
|
||||
int llabelqty; ///< number of local labels
|
||||
llabel_t *llabels; ///< local labels.
|
||||
///< this array shouldn't be accessed directly; name.hpp
|
||||
///< functions should be used instead.
|
||||
|
||||
int regargqty; ///< number of register arguments.
|
||||
///< During analysis IDA tries to guess the register
|
||||
///< arguments. It stores store the guessing outcome
|
||||
///< in this field. As soon as it determines the final
|
||||
///< function prototype, regargqty is set to zero.
|
||||
regarg_t *regargs; ///< unsorted array of register arguments.
|
||||
///< use ...regarg...() functions to access this array.
|
||||
///< regargs are destroyed when the full function
|
||||
///< type is determined.
|
||||
|
||||
int tailqty; ///< number of function tails
|
||||
range_t *tails; ///< array of tails, sorted by ea.
|
||||
///< use func_tail_iterator_t to access function tails.
|
||||
#ifndef SWIG
|
||||
};
|
||||
/// attributes of a function tail chunk
|
||||
struct
|
||||
{
|
||||
#endif // SWIG
|
||||
ea_t owner; ///< the address of the main function possessing this tail
|
||||
int refqty; ///< number of referers
|
||||
ea_t *referers; ///< array of referers (function start addresses).
|
||||
///< use func_parent_iterator_t to access the referers.
|
||||
#ifndef SWIG
|
||||
};
|
||||
};
|
||||
#endif // SWIG
|
||||
|
||||
func_t(ea_t start=0, ea_t end=0, flags_t f=0)
|
||||
: range_t(start, end), flags(f|FUNC_NORET_PENDING), frame(BADNODE),
|
||||
frsize(0), frregs(0), argsize(0), fpd(0), color(DEFCOLOR),
|
||||
pntqty(0), points(nullptr),
|
||||
regvarqty(0), regvars(nullptr),
|
||||
llabelqty(0), llabels(nullptr),
|
||||
regargqty(0), regargs(nullptr),
|
||||
tailqty(0), tails(nullptr)
|
||||
{
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(func_t);
|
||||
|
||||
/// Does function describe a function entry chunk?
|
||||
inline bool is_func_entry(const func_t *pfn) { return pfn != nullptr && (pfn->flags & FUNC_TAIL) == 0; }
|
||||
/// Does function describe a function tail chunk?
|
||||
inline bool is_func_tail(const func_t *pfn) { return pfn != nullptr && (pfn->flags & FUNC_TAIL) != 0; }
|
||||
|
||||
|
||||
/// Lock function pointer
|
||||
/// Locked pointers are guaranteed to remain valid until they are unlocked.
|
||||
/// Ranges with locked pointers cannot be deleted or moved.
|
||||
|
||||
idaman void ida_export lock_func_range(const func_t *pfn, bool lock);
|
||||
|
||||
/// Helper class to lock a function pointer so it stays valid
|
||||
class lock_func
|
||||
{
|
||||
const func_t *pfn;
|
||||
public:
|
||||
lock_func(const func_t *_pfn) : pfn(_pfn)
|
||||
{
|
||||
lock_func_range(pfn, true);
|
||||
}
|
||||
~lock_func(void)
|
||||
{
|
||||
lock_func_range(pfn, false);
|
||||
}
|
||||
};
|
||||
|
||||
/// Is the function pointer locked?
|
||||
|
||||
idaman bool ida_export is_func_locked(const func_t *pfn);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// F U N C T I O N S
|
||||
//--------------------------------------------------------------------
|
||||
/// Get pointer to function structure by address.
|
||||
/// \param ea any address in a function
|
||||
/// \return ptr to a function or nullptr.
|
||||
/// This function returns a function entry chunk.
|
||||
|
||||
idaman func_t *ida_export get_func(ea_t ea);
|
||||
|
||||
|
||||
/// Get the containing tail chunk of 'ea'.
|
||||
/// \retval -1 means 'does not contain ea'
|
||||
/// \retval 0 means the 'pfn' itself contains ea
|
||||
/// \retval >0 the number of the containing function tail chunk
|
||||
|
||||
idaman int ida_export get_func_chunknum(func_t *pfn, ea_t ea);
|
||||
|
||||
/// Does the given function contain the given address?
|
||||
|
||||
inline bool func_contains(func_t *pfn, ea_t ea)
|
||||
{
|
||||
return get_func_chunknum(pfn, ea) >= 0;
|
||||
}
|
||||
|
||||
/// Do two addresses belong to the same function?
|
||||
inline bool is_same_func(ea_t ea1, ea_t ea2)
|
||||
{
|
||||
func_t *pfn = get_func(ea1);
|
||||
return pfn != nullptr && func_contains(pfn, ea2);
|
||||
}
|
||||
|
||||
/// Get pointer to function structure by number.
|
||||
/// \param n number of function, is in range 0..get_func_qty()-1
|
||||
/// \return ptr to a function or nullptr.
|
||||
/// This function returns a function entry chunk.
|
||||
|
||||
idaman func_t *ida_export getn_func(size_t n);
|
||||
|
||||
|
||||
/// Get total number of functions in the program
|
||||
|
||||
idaman size_t ida_export get_func_qty(void);
|
||||
|
||||
|
||||
/// Get ordinal number of a function.
|
||||
/// \param ea any address in the function
|
||||
/// \return number of function (0..get_func_qty()-1).
|
||||
/// -1 means 'no function at the specified address'.
|
||||
|
||||
idaman int ida_export get_func_num(ea_t ea);
|
||||
|
||||
|
||||
/// Get pointer to the previous function.
|
||||
/// \param ea any address in the program
|
||||
/// \return ptr to function or nullptr if previous function doesn't exist
|
||||
|
||||
idaman func_t *ida_export get_prev_func(ea_t ea);
|
||||
|
||||
|
||||
/// Get pointer to the next function.
|
||||
/// \param ea any address in the program
|
||||
/// \return ptr to function or nullptr if next function doesn't exist
|
||||
|
||||
idaman func_t *ida_export get_next_func(ea_t ea);
|
||||
|
||||
|
||||
/// Get function ranges.
|
||||
/// \param ranges buffer to receive the range info
|
||||
/// \param pfn ptr to function structure
|
||||
/// \return end address of the last function range (BADADDR-error)
|
||||
|
||||
idaman ea_t ida_export get_func_ranges(rangeset_t *ranges, func_t *pfn);
|
||||
|
||||
|
||||
/// Get function comment.
|
||||
/// \param buf buffer for the comment
|
||||
/// \param pfn ptr to function structure
|
||||
/// \param repeatable get repeatable comment?
|
||||
/// \return size of comment or -1
|
||||
/// In fact this function works with function chunks too.
|
||||
|
||||
idaman ssize_t ida_export get_func_cmt(qstring *buf, const func_t *pfn, bool repeatable);
|
||||
|
||||
|
||||
/// Set function comment.
|
||||
/// This function works with function chunks too.
|
||||
/// \param pfn ptr to function structure
|
||||
/// \param cmt comment string, may be multiline (with '\n').
|
||||
/// Use empty str ("") to delete comment
|
||||
/// \param repeatable set repeatable comment?
|
||||
|
||||
idaman bool ida_export set_func_cmt(const func_t *pfn, const char *cmt, bool repeatable);
|
||||
|
||||
|
||||
/// Update information about a function in the database (::func_t).
|
||||
/// You must not change the function start and end addresses using this function.
|
||||
/// Use set_func_start() and set_func_end() for it.
|
||||
/// \param pfn ptr to function structure
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export update_func(func_t *pfn);
|
||||
|
||||
|
||||
/// Add a new function.
|
||||
/// If the fn->end_ea is #BADADDR, then IDA will try to determine the
|
||||
/// function bounds by calling find_func_bounds(..., #FIND_FUNC_DEFINE).
|
||||
/// \param pfn ptr to filled function structure
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export add_func_ex(func_t *pfn);
|
||||
|
||||
|
||||
/// Add a new function.
|
||||
/// If the function end address is #BADADDR, then IDA will try to determine
|
||||
/// the function bounds by calling find_func_bounds(..., #FIND_FUNC_DEFINE).
|
||||
/// \param ea1 start address
|
||||
/// \param ea2 end address
|
||||
/// \return success
|
||||
|
||||
inline bool add_func(ea_t ea1, ea_t ea2=BADADDR)
|
||||
{
|
||||
func_t fn(ea1, ea2);
|
||||
return add_func_ex(&fn);
|
||||
}
|
||||
|
||||
|
||||
/// Delete a function.
|
||||
/// \param ea any address in the function entry chunk
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export del_func(ea_t ea);
|
||||
|
||||
|
||||
/// Move function chunk start address.
|
||||
/// \param ea any address in the function
|
||||
/// \param newstart new end address of the function
|
||||
/// \return \ref MOVE_FUNC_
|
||||
|
||||
idaman int ida_export set_func_start(ea_t ea, ea_t newstart);
|
||||
/// \defgroup MOVE_FUNC_ Function move result codes
|
||||
/// Return values for set_func_start()
|
||||
//@{
|
||||
#define MOVE_FUNC_OK 0 ///< ok
|
||||
#define MOVE_FUNC_NOCODE 1 ///< no instruction at 'newstart'
|
||||
#define MOVE_FUNC_BADSTART 2 ///< bad new start address
|
||||
#define MOVE_FUNC_NOFUNC 3 ///< no function at 'ea'
|
||||
#define MOVE_FUNC_REFUSED 4 ///< a plugin refused the action
|
||||
//@}
|
||||
|
||||
|
||||
/// Move function chunk end address.
|
||||
/// \param ea any address in the function
|
||||
/// \param newend new end address of the function
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export set_func_end(ea_t ea, ea_t newend);
|
||||
|
||||
|
||||
/// Reanalyze a function.
|
||||
/// This function plans to analyzes all chunks of the given function.
|
||||
/// Optional parameters (ea1, ea2) may be used to narrow the analyzed range.
|
||||
/// \param pfn pointer to a function
|
||||
/// \param ea1 start of the range to analyze
|
||||
/// \param ea2 end of range to analyze
|
||||
/// \param analyze_parents meaningful only if pfn points to a function tail.
|
||||
/// if true, all tail parents will be reanalyzed.
|
||||
/// if false, only the given tail will be reanalyzed.
|
||||
|
||||
idaman void ida_export reanalyze_function(
|
||||
func_t *pfn,
|
||||
ea_t ea1=0,
|
||||
ea_t ea2=BADADDR,
|
||||
bool analyze_parents=false);
|
||||
|
||||
|
||||
/// Determine the boundaries of a new function.
|
||||
/// This function tries to find the start and end addresses of a new function.
|
||||
/// It calls the module with \ph{func_bounds} in order to fine tune
|
||||
/// the function boundaries.
|
||||
/// \param nfn structure to fill with information
|
||||
/// \ nfn->start_ea points to the start address of the new function.
|
||||
/// \param flags \ref FIND_FUNC_F
|
||||
/// \return \ref FIND_FUNC_R
|
||||
|
||||
idaman int ida_export find_func_bounds(func_t *nfn, int flags);
|
||||
|
||||
/// \defgroup FIND_FUNC_F Find function bounds flags
|
||||
/// Passed as 'flags' parameter to find_func_bounds()
|
||||
//@{
|
||||
#define FIND_FUNC_NORMAL 0x0000 ///< stop processing if undefined byte is encountered
|
||||
#define FIND_FUNC_DEFINE 0x0001 ///< create instruction if undefined byte is encountered
|
||||
#define FIND_FUNC_IGNOREFN 0x0002 ///< ignore existing function boundaries.
|
||||
///< by default the function returns function boundaries
|
||||
///< if ea belongs to a function.
|
||||
#define FIND_FUNC_KEEPBD 0x0004 ///< do not modify incoming function boundaries,
|
||||
///< just create instructions inside the boundaries.
|
||||
//@}
|
||||
|
||||
/// \defgroup FIND_FUNC_R Find function bounds result codes
|
||||
/// Return values for find_func_bounds()
|
||||
//@{
|
||||
#define FIND_FUNC_UNDEF 0 ///< function has instructions that pass execution flow to unexplored bytes.
|
||||
///< nfn->end_ea will have the address of the unexplored byte.
|
||||
#define FIND_FUNC_OK 1 ///< ok, 'nfn' is ready for add_func()
|
||||
#define FIND_FUNC_EXIST 2 ///< function exists already.
|
||||
///< its bounds are returned in 'nfn'.
|
||||
//@}
|
||||
|
||||
|
||||
/// Get function name.
|
||||
/// \param out buffer for the answer
|
||||
/// \param ea any address in the function
|
||||
/// \return length of the function name
|
||||
|
||||
idaman ssize_t ida_export get_func_name(qstring *out, ea_t ea);
|
||||
|
||||
|
||||
/// Calculate function size.
|
||||
/// This function takes into account all fragments of the function.
|
||||
/// \param pfn ptr to function structure
|
||||
|
||||
idaman asize_t ida_export calc_func_size(func_t *pfn);
|
||||
|
||||
|
||||
/// Get function bitness (which is equal to the function segment bitness).
|
||||
/// pfn==nullptr => returns 0
|
||||
/// \retval 0 16
|
||||
/// \retval 1 32
|
||||
/// \retval 2 64
|
||||
|
||||
idaman int ida_export get_func_bitness(const func_t *pfn);
|
||||
|
||||
/// Get number of bits in the function addressing
|
||||
inline int idaapi get_func_bits(const func_t *pfn) { return 1 << (get_func_bitness(pfn)+4); }
|
||||
|
||||
/// Get number of bytes in the function addressing
|
||||
inline int idaapi get_func_bytes(const func_t *pfn) { return get_func_bits(pfn)/8; }
|
||||
|
||||
|
||||
/// Is the function visible (not hidden)?
|
||||
|
||||
inline bool is_visible_func(func_t *pfn) { return (pfn->flags & FUNC_HIDDEN) == 0; }
|
||||
|
||||
/// Is the function visible (event after considering #SCF_SHHID_FUNC)?
|
||||
inline bool is_finally_visible_func(func_t *pfn)
|
||||
{
|
||||
return (inf_get_cmtflg() & SCF_SHHID_FUNC) != 0 || is_visible_func(pfn);
|
||||
}
|
||||
|
||||
/// Set visibility of function
|
||||
|
||||
idaman void ida_export set_visible_func(func_t *pfn, bool visible);
|
||||
|
||||
|
||||
/// Give a meaningful name to function if it consists of only 'jump' instruction.
|
||||
/// \param pfn pointer to function (may be nullptr)
|
||||
/// \param oldname old name of function.
|
||||
/// if old name was in "j_..." form, then we may discard it
|
||||
/// and set a new name.
|
||||
/// if oldname is not known, you may pass nullptr.
|
||||
/// \return success
|
||||
|
||||
idaman int ida_export set_func_name_if_jumpfunc(func_t *pfn, const char *oldname);
|
||||
|
||||
|
||||
/// Calculate target of a thunk function.
|
||||
/// \param pfn pointer to function (may not be nullptr)
|
||||
/// \param fptr out: will hold address of a function pointer (if indirect jump)
|
||||
/// \return the target function or #BADADDR
|
||||
|
||||
idaman ea_t ida_export calc_thunk_func_target(func_t *pfn, ea_t *fptr);
|
||||
|
||||
|
||||
/// Does the function return?.
|
||||
/// To calculate the answer, #FUNC_NORET flag and is_noret() are consulted
|
||||
/// The latter is required for imported functions in the .idata section.
|
||||
/// Since in .idata we have only function pointers but not functions, we have
|
||||
/// to introduce a special flag for them.
|
||||
|
||||
idaman bool ida_export func_does_return(ea_t callee);
|
||||
|
||||
|
||||
/// Plan to reanalyze noret flag.
|
||||
/// This function does not remove FUNC_NORET if it is already present.
|
||||
/// It just plans to reanalysis.
|
||||
|
||||
idaman bool ida_export reanalyze_noret_flag(ea_t ea);
|
||||
|
||||
|
||||
/// Signal a non-returning instruction.
|
||||
/// This function can be used by the processor module to tell the kernel
|
||||
/// about non-returning instructions (like call exit). The kernel will
|
||||
/// perform the global function analysis and find out if the function
|
||||
/// returns at all. This analysis will be done at the first call to func_does_return()
|
||||
/// \return true if the instruction 'noret' flag has been changed
|
||||
|
||||
idaman bool ida_export set_noret_insn(ea_t insn_ea, bool noret);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// F U N C T I O N C H U N K S
|
||||
//--------------------------------------------------------------------
|
||||
/// Get pointer to function chunk structure by address.
|
||||
/// \param ea any address in a function chunk
|
||||
/// \return ptr to a function chunk or nullptr.
|
||||
/// This function may return a function entry as well as a function tail.
|
||||
|
||||
idaman func_t *ida_export get_fchunk(ea_t ea);
|
||||
|
||||
|
||||
/// Get pointer to function chunk structure by number.
|
||||
/// \param n number of function chunk, is in range 0..get_fchunk_qty()-1
|
||||
/// \return ptr to a function chunk or nullptr.
|
||||
/// This function may return a function entry as well as a function tail.
|
||||
|
||||
idaman func_t *ida_export getn_fchunk(int n);
|
||||
|
||||
|
||||
/// Get total number of function chunks in the program
|
||||
|
||||
idaman size_t ida_export get_fchunk_qty(void);
|
||||
|
||||
|
||||
/// Get ordinal number of a function chunk in the global list of function chunks.
|
||||
/// \param ea any address in the function chunk
|
||||
/// \return number of function chunk (0..get_fchunk_qty()-1).
|
||||
/// -1 means 'no function chunk at the specified address'.
|
||||
|
||||
idaman int ida_export get_fchunk_num(ea_t ea);
|
||||
|
||||
|
||||
/// Get pointer to the previous function chunk in the global list.
|
||||
/// \param ea any address in the program
|
||||
/// \return ptr to function chunk or nullptr if previous function chunk doesn't exist
|
||||
|
||||
idaman func_t *ida_export get_prev_fchunk(ea_t ea);
|
||||
|
||||
|
||||
/// Get pointer to the next function chunk in the global list.
|
||||
/// \param ea any address in the program
|
||||
/// \return ptr to function chunk or nullptr if next function chunk doesn't exist
|
||||
|
||||
idaman func_t *ida_export get_next_fchunk(ea_t ea);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Functions to manipulate function chunks
|
||||
|
||||
/// Append a new tail chunk to the function definition.
|
||||
/// If the tail already exists, then it will simply be added to the function tail list
|
||||
/// Otherwise a new tail will be created and its owner will be set to be our function
|
||||
/// If a new tail cannot be created, then this function will fail.
|
||||
/// \param ea1 start of the tail. If a tail already exists at the specified address
|
||||
/// it must start at 'ea1'
|
||||
/// \param ea2 end of the tail. If a tail already exists at the specified address
|
||||
/// it must end at 'ea2'. If specified as BADADDR, IDA will determine
|
||||
/// the end address itself.
|
||||
|
||||
idaman bool ida_export append_func_tail(func_t *pfn, ea_t ea1, ea_t ea2);
|
||||
|
||||
|
||||
/// Remove a function tail.
|
||||
/// If the tail belongs only to one function, it will be completely removed.
|
||||
/// Otherwise if the function was the tail owner, the first function using
|
||||
/// this tail becomes the owner of the tail.
|
||||
|
||||
idaman bool ida_export remove_func_tail(func_t *pfn, ea_t tail_ea);
|
||||
|
||||
|
||||
/// Set a function as the possessing function of a function tail.
|
||||
/// The function should already refer to the tail (after append_func_tail).
|
||||
|
||||
idaman bool ida_export set_tail_owner(func_t *fnt, ea_t func_start);
|
||||
|
||||
|
||||
// Auxiliary function(s) to be used in func_..._iterator_t
|
||||
|
||||
class func_parent_iterator_t;
|
||||
class func_tail_iterator_t;
|
||||
class func_item_iterator_t;
|
||||
|
||||
/// Declare helper functions for ::func_item_iterator_t
|
||||
#define DECLARE_FUNC_ITERATORS(prefix) \
|
||||
prefix bool ida_export func_tail_iterator_set(func_tail_iterator_t *fti, func_t *pfn, ea_t ea);\
|
||||
prefix bool ida_export func_tail_iterator_set_ea(func_tail_iterator_t *fti, ea_t ea);\
|
||||
prefix bool ida_export func_parent_iterator_set(func_parent_iterator_t *fpi, func_t *pfn);\
|
||||
prefix bool ida_export func_item_iterator_next(func_item_iterator_t *fii, testf_t *testf, void *ud);\
|
||||
prefix bool ida_export func_item_iterator_prev(func_item_iterator_t *fii, testf_t *testf, void *ud);\
|
||||
prefix bool ida_export func_item_iterator_decode_prev_insn(func_item_iterator_t *fii, insn_t *out); \
|
||||
prefix bool ida_export func_item_iterator_decode_preceding_insn(func_item_iterator_t *fii, eavec_t *visited, bool *p_farref, insn_t *out);
|
||||
DECLARE_FUNC_ITERATORS(idaman)
|
||||
|
||||
/// Helper function to accept any address
|
||||
inline THREAD_SAFE bool idaapi f_any(flags_t, void *) { return true; }
|
||||
|
||||
/// Class to enumerate all function tails sorted by addresses.
|
||||
/// Enumeration is started with main(), first(), or last().
|
||||
/// If first() is used, the function entry chunk will be excluded from the enumeration.
|
||||
/// Otherwise it will be included in the enumeration (for main() and last()).
|
||||
/// The loop may continue until the next() or prev() function returns false.
|
||||
/// These functions return false when the enumeration is over.
|
||||
/// The tail chunks are always sorted by their addresses.
|
||||
///
|
||||
/// Sample code:
|
||||
/// \code
|
||||
/// func_tail_iterator_t fti(pfn);
|
||||
/// for ( bool ok=fti.first(); ok; ok=fti.next() )
|
||||
/// const range_t &a = fti.chunk();
|
||||
/// ....
|
||||
/// \endcode
|
||||
///
|
||||
/// If the 'ea' parameter is used in the constructor, then the iterator is positioned
|
||||
/// at the chunk containing the specified 'ea'. Otherwise it is positioned at the
|
||||
/// function entry chunk.
|
||||
/// If 'pfn' is specified as nullptr then the set() function will fail,
|
||||
/// but it is still possible to use the class. In this case the iteration will be
|
||||
/// limited by the segment boundaries.
|
||||
/// The function main chunk is locked during the iteration.
|
||||
/// It is also possible to enumerate one single arbitrary range using set_range()
|
||||
/// This function is mainly designed to be used from ::func_item_iterator_t.
|
||||
class func_tail_iterator_t
|
||||
{
|
||||
func_t *pfn;
|
||||
int idx;
|
||||
range_t seglim; // valid and used only if pfn == nullptr
|
||||
public:
|
||||
func_tail_iterator_t(void) : pfn(nullptr), idx(-1) {}
|
||||
func_tail_iterator_t(func_t *_pfn, ea_t ea=BADADDR) : pfn(nullptr) { set(_pfn, ea); }
|
||||
~func_tail_iterator_t(void)
|
||||
{
|
||||
// if was iterating over function chunks, unlock the main chunk
|
||||
if ( pfn != nullptr )
|
||||
lock_func_range(pfn, false);
|
||||
}
|
||||
bool set(func_t *_pfn, ea_t ea=BADADDR) { return func_tail_iterator_set(this, _pfn, ea); }
|
||||
bool set_ea(ea_t ea) { return func_tail_iterator_set_ea(this, ea); }
|
||||
// set an arbitrary range
|
||||
bool set_range(ea_t ea1, ea_t ea2)
|
||||
{
|
||||
this->~func_tail_iterator_t();
|
||||
pfn = nullptr;
|
||||
idx = -1;
|
||||
seglim = range_t(ea1, ea2);
|
||||
return !seglim.empty();
|
||||
}
|
||||
const range_t &chunk(void) const
|
||||
{
|
||||
if ( pfn == nullptr )
|
||||
return seglim;
|
||||
return idx >= 0 && idx < pfn->tailqty ? pfn->tails[idx] : *(range_t*)pfn;
|
||||
}
|
||||
bool first(void) { if ( pfn != nullptr ) { idx = 0; return pfn->tailqty > 0; } return false; } // get only tail chunks
|
||||
bool last(void) { if ( pfn != nullptr ) { idx = pfn->tailqty - 1; return true; } return false; } // get all chunks (the entry chunk last)
|
||||
bool next(void) { if ( pfn != nullptr && idx+1 < pfn->tailqty ) { idx++; return true; } return false; }
|
||||
bool prev(void) { if ( idx >= 0 ) { idx--; return true; } return false; }
|
||||
bool main(void) { idx = -1; return pfn != nullptr; } // get all chunks (the entry chunk first)
|
||||
};
|
||||
|
||||
|
||||
/// Function to iterate function chunks (all of them including the entry chunk)
|
||||
/// \param pfn pointer to the function
|
||||
/// \param func function to call for each chunk
|
||||
/// \param ud user data for 'func'
|
||||
/// \param include_parents meaningful only if pfn points to a function tail.
|
||||
/// if true, all tail parents will be iterated.
|
||||
/// if false, only the given tail will be iterated.
|
||||
|
||||
idaman void ida_export iterate_func_chunks(
|
||||
func_t *pfn,
|
||||
void (idaapi *func)(ea_t ea1, ea_t ea2, void *ud),
|
||||
void *ud=nullptr,
|
||||
bool include_parents=false);
|
||||
|
||||
|
||||
/// Class to enumerate all function instructions and data sorted by addresses.
|
||||
/// The function entry chunk items are enumerated first regardless of their addresses
|
||||
///
|
||||
/// Sample code:
|
||||
/// \code
|
||||
/// func_item_iterator_t fii;
|
||||
/// for ( bool ok=fii.set(pfn, ea); ok; ok=fii.next_addr() )
|
||||
/// ea_t ea = fii.current();
|
||||
/// ....
|
||||
/// \endcode
|
||||
///
|
||||
/// If 'ea' is not specified in the call to set(), then the enumeration starts at
|
||||
/// the function entry point.
|
||||
/// If 'pfn' is specified as nullptr then the set() function will fail,
|
||||
/// but it is still possible to use the class. In this case the iteration will be
|
||||
/// limited by the segment boundaries.
|
||||
/// It is also possible to enumerate addresses in an arbitrary range using set_range().
|
||||
class func_item_iterator_t
|
||||
{
|
||||
func_tail_iterator_t fti;
|
||||
ea_t ea;
|
||||
public:
|
||||
func_item_iterator_t(void) : ea(BADADDR) {}
|
||||
func_item_iterator_t(func_t *pfn, ea_t _ea=BADADDR) { set(pfn, _ea); }
|
||||
/// Set a function range. if pfn == nullptr then a segment range will be set.
|
||||
bool set(func_t *pfn, ea_t _ea=BADADDR)
|
||||
{
|
||||
ea = (_ea != BADADDR || pfn == nullptr) ? _ea : pfn->start_ea;
|
||||
return fti.set(pfn, _ea);
|
||||
}
|
||||
/// Set an arbitrary range
|
||||
bool set_range(ea_t ea1, ea_t ea2) { ea = ea1; return fti.set_range(ea1, ea2); }
|
||||
bool first(void) { if ( !fti.main() ) return false; ea=fti.chunk().start_ea; return true; }
|
||||
bool last(void) { if ( !fti.last() ) return false; ea=fti.chunk().end_ea; return true; }
|
||||
ea_t current(void) const { return ea; }
|
||||
const range_t &chunk(void) const { return fti.chunk(); }
|
||||
bool next(testf_t *func, void *ud) { return func_item_iterator_next(this, func, ud); }
|
||||
bool prev(testf_t *func, void *ud) { return func_item_iterator_prev(this, func, ud); }
|
||||
bool next_addr(void) { return next(f_any, nullptr); }
|
||||
bool next_head(void) { return next(f_is_head, nullptr); }
|
||||
bool next_code(void) { return next(f_is_code, nullptr); }
|
||||
bool next_data(void) { return next(f_is_data, nullptr); }
|
||||
bool next_not_tail(void) { return next(f_is_not_tail, nullptr); }
|
||||
bool prev_addr(void) { return prev(f_any, nullptr); }
|
||||
bool prev_head(void) { return prev(f_is_head, nullptr); }
|
||||
bool prev_code(void) { return prev(f_is_code, nullptr); }
|
||||
bool prev_data(void) { return prev(f_is_data, nullptr); }
|
||||
bool prev_not_tail(void) { return prev(f_is_not_tail, nullptr); }
|
||||
bool decode_prev_insn(insn_t *out) { return func_item_iterator_decode_prev_insn(this, out); }
|
||||
bool decode_preceding_insn(eavec_t *visited, bool *p_farref, insn_t *out)
|
||||
{ return func_item_iterator_decode_preceding_insn(this, visited, p_farref, out); }
|
||||
};
|
||||
|
||||
/// Class to enumerate all function parents sorted by addresses.
|
||||
/// Enumeration is started with first() or last().
|
||||
/// The loop may continue until the next() or prev() function returns false.
|
||||
/// The parent functions are always sorted by their addresses.
|
||||
/// The tail chunk is locked during the iteration.
|
||||
///
|
||||
/// Sample code:
|
||||
/// \code
|
||||
/// func_parent_iterator_t fpi(fnt);
|
||||
/// for ( bool ok=fpi.first(); ok; ok=fpi.next() )
|
||||
/// ea_t parent = fpi.parent();
|
||||
/// ....
|
||||
/// \endcode
|
||||
class func_parent_iterator_t
|
||||
{
|
||||
func_t *fnt;
|
||||
int idx;
|
||||
public:
|
||||
func_parent_iterator_t(void) : fnt(nullptr), idx(0) {}
|
||||
func_parent_iterator_t(func_t *_fnt) : fnt(nullptr) { set(_fnt); }
|
||||
~func_parent_iterator_t(void)
|
||||
{
|
||||
if ( fnt != nullptr )
|
||||
lock_func_range(fnt, false);
|
||||
}
|
||||
bool set(func_t *_fnt) { return func_parent_iterator_set(this, _fnt); }
|
||||
ea_t parent(void) const { return fnt->referers[idx]; }
|
||||
bool first(void) { idx = 0; return is_func_tail(fnt) && fnt->refqty > 0; }
|
||||
bool last(void) { idx = fnt->refqty - 1; return idx >= 0; }
|
||||
bool next(void) { if ( idx+1 < fnt->refqty ) { idx++; return true; } return false; }
|
||||
bool prev(void) { if ( idx > 0 ) { idx--; return true; } return false; }
|
||||
void reset_fnt(func_t *_fnt) { fnt = _fnt; } // for internal use only!
|
||||
};
|
||||
|
||||
|
||||
/// \name Get prev/next address in function
|
||||
/// Unlike func_item_iterator_t which always enumerates the main function
|
||||
/// chunk first, these functions respect linear address ordering.
|
||||
//@{
|
||||
idaman ea_t ida_export get_prev_func_addr(func_t *pfn, ea_t ea);
|
||||
idaman ea_t ida_export get_next_func_addr(func_t *pfn, ea_t ea);
|
||||
//@}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
/// \name
|
||||
/// Functions to work with temporary register argument definitions
|
||||
//@{
|
||||
idaman void ida_export read_regargs(func_t *pfn);
|
||||
idaman void ida_export add_regarg(func_t *pfn, int reg, const tinfo_t &tif, const char *name);
|
||||
//@}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// L I B R A R Y M O D U L E S I G N A T U R E S
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/// \defgroup IDASGN_ Error codes for signature functions:
|
||||
/// See calc_idasgn_state() and del_idasgn()
|
||||
//@{
|
||||
#define IDASGN_OK 0 ///< ok
|
||||
#define IDASGN_BADARG 1 ///< bad number of signature
|
||||
#define IDASGN_APPLIED 2 ///< signature is already applied
|
||||
#define IDASGN_CURRENT 3 ///< signature is currently being applied
|
||||
#define IDASGN_PLANNED 4 ///< signature is planned to be applied
|
||||
//@}
|
||||
|
||||
/// Add a signature file to the list of planned signature files.
|
||||
/// \param fname file name. should not contain directory part.
|
||||
/// \return 0 if failed, otherwise number of planned (and applied) signatures
|
||||
|
||||
idaman int ida_export plan_to_apply_idasgn(const char *fname); // plan to use library
|
||||
|
||||
|
||||
/// Apply a signature file to the specified address.
|
||||
/// \param signame short name of signature file (the file name without path)
|
||||
/// \param ea address to apply the signature
|
||||
/// \param is_startup if set, then the signature is treated as a startup one
|
||||
/// for startup signature ida doesn't rename the first
|
||||
/// function of the applied module.
|
||||
/// \return \ref LIBFUNC_
|
||||
|
||||
idaman int ida_export apply_idasgn_to(const char *signame, ea_t ea, bool is_startup);
|
||||
|
||||
|
||||
/// Get number of signatures in the list of planned and applied signatures.
|
||||
/// \return 0..n
|
||||
|
||||
idaman int ida_export get_idasgn_qty(void);
|
||||
|
||||
|
||||
/// Get number of the the current signature.
|
||||
/// \return 0..n-1
|
||||
|
||||
idaman int ida_export get_current_idasgn(void);
|
||||
|
||||
|
||||
/// Get state of a signature in the list of planned signatures
|
||||
/// \param n number of signature in the list (0..get_idasgn_qty()-1)
|
||||
/// \return state of signature or #IDASGN_BADARG
|
||||
|
||||
idaman int ida_export calc_idasgn_state(int n);
|
||||
|
||||
|
||||
/// Remove signature from the list of planned signatures.
|
||||
/// \param n number of signature in the list (0..get_idasgn_qty()-1)
|
||||
/// \return #IDASGN_OK, #IDASGN_BADARG, #IDASGN_APPLIED
|
||||
|
||||
idaman int ida_export del_idasgn(int n);
|
||||
|
||||
|
||||
/// Get information about a signature in the list.
|
||||
/// \param signame buffer for the name of the signature.
|
||||
/// (short form, only base name without the directory part
|
||||
/// will be stored).
|
||||
/// if signame == nullptr, then the name won't be returned.
|
||||
/// \param optlibs buffer for the names of the optional libraries
|
||||
/// if optlibs == nullptr, then the optional libraries are not returned
|
||||
/// \param n number of signature in the list (0..get_idasgn_qty()-1)
|
||||
/// \return number of successfully recognized modules using this signature.
|
||||
/// -1 means the 'n' is a bad argument, i.e. no signature with this
|
||||
/// number exists..
|
||||
|
||||
idaman int32 ida_export get_idasgn_desc(
|
||||
qstring *signame,
|
||||
qstring *optlibs,
|
||||
int n);
|
||||
|
||||
|
||||
class idasgn_t;
|
||||
|
||||
/// Get idasgn header by a short signature name.
|
||||
/// \param name short name of a signature
|
||||
/// \return nullptr if can't find the signature
|
||||
|
||||
idaman idasgn_t *ida_export get_idasgn_header_by_short_name(const char *name);
|
||||
|
||||
|
||||
/// Get full description of the signature by its short name.
|
||||
/// \param buf the output buffer
|
||||
/// \param name short name of a signature
|
||||
/// \return size of signature description or -1
|
||||
|
||||
idaman ssize_t ida_export get_idasgn_title(
|
||||
qstring *buf,
|
||||
const char *name);
|
||||
|
||||
/// Determine compiler/vendor using the startup signatures.
|
||||
/// If determined, then appropriate signature files are included into
|
||||
/// the list of planned signature files.
|
||||
|
||||
idaman void ida_export determine_rtl(void);
|
||||
|
||||
|
||||
/// Apply a startup signature file to the specified address.
|
||||
/// \param ea address to apply the signature to; usually \inf{start_ea}
|
||||
/// \param startup the name of the signature file without path and extension
|
||||
/// \return true if successfully applied the signature
|
||||
|
||||
idaman bool ida_export apply_startup_sig(ea_t ea, const char *startup);
|
||||
|
||||
|
||||
/// Apply the currently loaded signature file to the specified address.
|
||||
/// If a library function is found, then create a function and name
|
||||
/// it accordingly.
|
||||
/// \param ea any address in the program
|
||||
/// \returns \ref LIBFUNC_
|
||||
|
||||
idaman int ida_export try_to_add_libfunc(ea_t ea);
|
||||
|
||||
|
||||
/// \defgroup LIBFUNC_ Library function codes
|
||||
/// Return values for try_to_add_libfunc() and apply_idasgn_to()
|
||||
//@{
|
||||
#define LIBFUNC_FOUND 0 ///< ok, library function is found
|
||||
#define LIBFUNC_NONE 1 ///< no, this is not a library function
|
||||
#define LIBFUNC_DELAY 2 ///< no decision because of lack of information
|
||||
//@}
|
||||
|
||||
// KERNEL mode functions
|
||||
|
||||
/// \cond
|
||||
/// kept in the sdk because inlined
|
||||
inline void save_signatures(void) {}
|
||||
bool invalidate_sp_analysis(func_t *pfn);
|
||||
inline bool invalidate_sp_analysis(ea_t ea)
|
||||
{ return invalidate_sp_analysis(get_func(ea)); }
|
||||
/// \endcond
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,368 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*
|
||||
* Graph drawing support
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __GDLDRAW_HPP
|
||||
#define __GDLDRAW_HPP
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <funcs.hpp>
|
||||
|
||||
/*! \file gdl.hpp
|
||||
|
||||
\brief Low level graph drawing operations
|
||||
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// forward declarations:
|
||||
class node_iterator;
|
||||
class qflow_chart_t;
|
||||
class gdl_graph_t;
|
||||
|
||||
/// Flow chart block types
|
||||
enum fc_block_type_t
|
||||
{
|
||||
fcb_normal, ///< normal block
|
||||
fcb_indjump, ///< block ends with indirect jump
|
||||
fcb_ret, ///< return block
|
||||
fcb_cndret, ///< conditional return block
|
||||
fcb_noret, ///< noreturn block
|
||||
fcb_enoret, ///< external noreturn block (does not belong to the function)
|
||||
fcb_extern, ///< external normal block
|
||||
fcb_error, ///< block passes execution past the function end
|
||||
};
|
||||
|
||||
#ifndef SWIG
|
||||
#define DECLARE_HELPER(decl) \
|
||||
decl node_iterator *ida_export node_iterator_goup(node_iterator *); \
|
||||
decl void ida_export create_qflow_chart(qflow_chart_t &); \
|
||||
decl bool ida_export append_to_flowchart(qflow_chart_t &, ea_t, ea_t); \
|
||||
decl fc_block_type_t ida_export fc_calc_block_type(const qflow_chart_t &, size_t); \
|
||||
decl bool ida_export create_multirange_qflow_chart(qflow_chart_t &, const rangevec_t &);
|
||||
#else
|
||||
#define DECLARE_HELPER(decl)
|
||||
#endif // SWIG
|
||||
|
||||
DECLARE_HELPER(idaman)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// Set of integer constants
|
||||
class intset_t : public std::set<int>
|
||||
{
|
||||
public:
|
||||
DEFINE_MEMORY_ALLOCATION_FUNCS()
|
||||
size_t idaapi print(char *buf, size_t bufsize) const;
|
||||
const char *idaapi dstr(void) const;
|
||||
bool has(int value) const
|
||||
{
|
||||
const_iterator p = find(value);
|
||||
const_iterator q = end();
|
||||
return p != q;
|
||||
}
|
||||
};
|
||||
|
||||
typedef qvector<intvec_t> array_of_intvec_t;
|
||||
|
||||
/// Map of integer constants to integer constants
|
||||
class intmap_t : public std::map<int, int>
|
||||
{
|
||||
public:
|
||||
DEFINE_MEMORY_ALLOCATION_FUNCS()
|
||||
size_t idaapi print(char *buf, size_t bufsize) const;
|
||||
const char *idaapi dstr(void) const;
|
||||
};
|
||||
|
||||
typedef qvector<intmap_t> array_of_intmap_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// Set of graph nodes
|
||||
class node_set_t : public intset_t
|
||||
{
|
||||
public:
|
||||
idaapi node_set_t(void) {}
|
||||
idaapi node_set_t(int node) { insert(node); }
|
||||
idaapi node_set_t(const gdl_graph_t *g);
|
||||
bool idaapi add(int node) { return insert(node).second; }
|
||||
void idaapi sub(int node) { erase(node); }
|
||||
void idaapi sub(const node_set_t &r);
|
||||
void idaapi add(const node_set_t &r);
|
||||
void idaapi intersect(const node_set_t &r);
|
||||
void idaapi extract(intvec_t &out) const;
|
||||
int idaapi first(void) const { return empty() ? -1 : *begin(); }
|
||||
};
|
||||
|
||||
typedef qvector<node_set_t> array_of_node_set_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// Node iterator (used to draw graphs)
|
||||
class node_iterator
|
||||
{
|
||||
DECLARE_HELPER(friend)
|
||||
friend class gdl_graph_t;
|
||||
const gdl_graph_t *g;
|
||||
int i;
|
||||
node_iterator &_goup(void);
|
||||
node_iterator &goup(void) { return *node_iterator_goup(this); }
|
||||
public:
|
||||
node_iterator(const gdl_graph_t *_g, int n) : g(_g), i(n) {}
|
||||
node_iterator &operator++(void) { i++; return goup(); }
|
||||
bool operator==(const node_iterator &n) const { return i == n.i && g == n.g; }
|
||||
bool operator!=(const node_iterator &n) const { return !(*this == n); }
|
||||
int operator*(void) const { return i; }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// gdl graph interface - includes only functions required to draw it
|
||||
class gdl_graph_t
|
||||
{
|
||||
// does a path from 'm' to 'n' exist?
|
||||
bool idaapi path(node_set_t &visited, int m, int n) const;
|
||||
public:
|
||||
DEFINE_MEMORY_ALLOCATION_FUNCS()
|
||||
DEFINE_VIRTUAL_DTOR(gdl_graph_t)
|
||||
virtual char *idaapi get_node_label(char *buf, int /*bufsize*/, int /*n*/) const { buf[0] = '\0'; return buf; }
|
||||
virtual void idaapi print_graph_attributes(FILE * /*fp*/) const {}
|
||||
virtual bool idaapi print_node(FILE * /*fp*/, int /*n*/) const { return false; }
|
||||
virtual bool idaapi print_edge(FILE * /*fp*/, int /*i*/, int /*j*/) const { return false; }
|
||||
virtual void idaapi print_node_attributes(FILE * /*fp*/, int /*n*/) const {}
|
||||
virtual int idaapi size(void) const = 0; // number of the max node number
|
||||
virtual int idaapi node_qty(void) const { return size(); } // number of alive nodes
|
||||
virtual bool idaapi exists(int /*node*/) const { return true; }
|
||||
virtual int idaapi entry(void) const { return 0; }
|
||||
virtual int idaapi exit(void) const { return size()-1; }
|
||||
virtual int idaapi nsucc(int node) const = 0;
|
||||
virtual int idaapi npred(int node) const = 0;
|
||||
virtual int idaapi succ(int node, int i) const = 0;
|
||||
virtual int idaapi pred(int node, int i) const = 0;
|
||||
virtual bool idaapi empty(void) const { return node_qty() == 0; }
|
||||
virtual bgcolor_t idaapi get_node_color(int /*n*/) const { return DEFCOLOR; }
|
||||
virtual bgcolor_t idaapi get_edge_color(int /*i*/, int /*j*/) const { return DEFCOLOR; }
|
||||
void idaapi gen_gdl(FILE *fp) const;
|
||||
void idaapi gen_gdl(const char *file) const;
|
||||
size_t idaapi nedge(int node, bool ispred) const { return ispred ? npred(node) : nsucc(node); }
|
||||
int idaapi edge(int node, int i, bool ispred) const { return ispred ? pred(node, i) : succ(node, i); }
|
||||
int idaapi front(void) { return *begin(); }
|
||||
node_iterator idaapi begin(void) const { return node_iterator(this, 0).goup(); }
|
||||
node_iterator idaapi end(void) const { return node_iterator(this, size()); }
|
||||
// does a path from 'm' to 'n' exist?
|
||||
bool idaapi path_exists(int m, int n) const { node_set_t v; return path(v, m, n); }
|
||||
|
||||
void idaapi gen_dot(FILE *fp) const;
|
||||
void idaapi gen_dot(const char *file) const;
|
||||
};
|
||||
|
||||
|
||||
/// Create GDL file for graph
|
||||
|
||||
idaman void ida_export gen_gdl(const gdl_graph_t *g, const char *fname);
|
||||
|
||||
|
||||
/// Display GDL file by calling wingraph32.
|
||||
/// The exact name of the grapher is taken from the configuration file
|
||||
/// and set up by setup_graph_subsystem().
|
||||
/// \return error code from os, 0 if ok
|
||||
|
||||
idaman int ida_export display_gdl(const char *fname);
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Build and display program graphs
|
||||
|
||||
/// Build and display a flow graph.
|
||||
/// \param filename output file name. the file extension is not used. maybe NULL.
|
||||
/// \param title graph title
|
||||
/// \param pfn function to graph
|
||||
/// \param ea1, ea2 if pfn == NULL, then the address range
|
||||
/// \param gflags combination of \ref CHART_1.
|
||||
/// if none of #CHART_GEN_DOT, #CHART_GEN_GDL, #CHART_WINGRAPH
|
||||
/// is specified, the function will return false
|
||||
/// \return success. if fails, a warning message is displayed on the screen
|
||||
|
||||
idaman bool ida_export gen_flow_graph(
|
||||
const char *filename,
|
||||
const char *title,
|
||||
func_t *pfn,
|
||||
ea_t ea1,
|
||||
ea_t ea2,
|
||||
int gflags);
|
||||
|
||||
/// \defgroup CHART_1 Flow graph building flags
|
||||
/// Passed as flags parameter to:
|
||||
/// - gen_flow_graph()
|
||||
/// - gen_simple_call_chart()
|
||||
/// - gen_complex_call_chart()
|
||||
//@{
|
||||
#define CHART_PRINT_NAMES 0x1000 ///< print labels for each block?
|
||||
#define CHART_GEN_DOT 0x2000 ///< generate .dot file (file extension is forced to .dot)
|
||||
#define CHART_GEN_GDL 0x4000 ///< generate .gdl file (file extension is forced to .gdl)
|
||||
#define CHART_WINGRAPH 0x8000 ///< call grapher to display the graph
|
||||
//@}
|
||||
|
||||
|
||||
/// Build and display a simple function call graph.
|
||||
/// \param filename output file name. the file extension is not used. maybe NULL.
|
||||
/// \param wait message to display during graph building
|
||||
/// \param title graph title
|
||||
/// \param gflags combination of #CHART_NOLIBFUNCS and \ref CHART_1.
|
||||
/// if none of #CHART_GEN_DOT, #CHART_GEN_GDL, #CHART_WINGRAPH
|
||||
/// is specified, the function will return false.
|
||||
/// \return success. if fails, a warning message is displayed on the screen
|
||||
|
||||
idaman bool ida_export gen_simple_call_chart(
|
||||
const char *filename,
|
||||
const char *wait,
|
||||
const char *title,
|
||||
int gflags);
|
||||
|
||||
|
||||
/// Build and display a complex xref graph.
|
||||
/// \param filename output file name. the file extension is not used. maybe NULL.
|
||||
/// \param wait message to display during graph building
|
||||
/// \param title graph title
|
||||
/// \param ea1, ea2 address range
|
||||
/// \param flags combination of \ref CHART_2 and \ref CHART_1.
|
||||
/// if none of #CHART_GEN_DOT, #CHART_GEN_GDL, #CHART_WINGRAPH
|
||||
/// is specified, the function will return false.
|
||||
/// \param recursion_depth optional limit of recursion
|
||||
/// \return success. if fails, a warning message is displayed on the screen
|
||||
|
||||
idaman bool ida_export gen_complex_call_chart(
|
||||
const char *filename,
|
||||
const char *wait,
|
||||
const char *title,
|
||||
ea_t ea1,
|
||||
ea_t ea2,
|
||||
int flags,
|
||||
int32 recursion_depth=-1);
|
||||
|
||||
/// \defgroup CHART_2 Call chart building flags
|
||||
/// Passed as flags parameter to gen_complex_call_chart()
|
||||
//@{
|
||||
#define CHART_NOLIBFUNCS 0x0400 ///< don't include library functions in the graph
|
||||
#define CHART_REFERENCING 0x0001 ///< references to the addresses in the list
|
||||
#define CHART_REFERENCED 0x0002 ///< references from the addresses in the list
|
||||
#define CHART_RECURSIVE 0x0004 ///< analyze added blocks
|
||||
#define CHART_FOLLOW_DIRECTION 0x0008 ///< analyze references to added blocks only in the direction of the reference who discovered the current block
|
||||
#define CHART_IGNORE_XTRN 0x0010
|
||||
#define CHART_IGNORE_DATA_BSS 0x0020
|
||||
#define CHART_IGNORE_LIB_TO 0x0040 ///< ignore references to library functions
|
||||
#define CHART_IGNORE_LIB_FROM 0x0080 ///< ignore references from library functions
|
||||
#define CHART_PRINT_COMMENTS 0x0100
|
||||
#define CHART_PRINT_DOTS 0x0200 ///< print dots if xrefs exist outside of the range recursion depth
|
||||
//@}
|
||||
|
||||
|
||||
/// Setup the user-defined graph colors and graph viewer program.
|
||||
/// This function is called by the GUI at the beginning, so no need to call
|
||||
/// it again.
|
||||
|
||||
idaman void ida_export setup_graph_subsystem(const char *_grapher, bgcolor_t (idaapi *get_graph_color)(int color));
|
||||
|
||||
|
||||
//-V:cancellable_graph_t:730 not all members of a class are initialized inside the constructor
|
||||
class cancellable_graph_t : public gdl_graph_t
|
||||
{
|
||||
public:
|
||||
mutable bool cancelled;
|
||||
char padding[3]; // make the class nicely aligned. otherwise we have problems
|
||||
// with gcc in qflow_chart_t.
|
||||
cancellable_graph_t(void) : cancelled(false) {}
|
||||
DEFINE_VIRTUAL_DTOR(cancellable_graph_t)
|
||||
bool idaapi check_cancel(void) const;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Information about a basic block of a \ref qflow_chart_t
|
||||
struct qbasic_block_t : public range_t
|
||||
{
|
||||
intvec_t succ; ///< list of node successors
|
||||
intvec_t pred; ///< list of node predecessors
|
||||
};
|
||||
|
||||
/// Does this block never return?
|
||||
inline THREAD_SAFE bool is_noret_block(fc_block_type_t btype)
|
||||
{
|
||||
return btype == fcb_noret || btype == fcb_enoret;
|
||||
}
|
||||
|
||||
/// Does this block return?
|
||||
inline THREAD_SAFE bool is_ret_block(fc_block_type_t btype)
|
||||
{
|
||||
return btype == fcb_ret || btype == fcb_cndret;
|
||||
}
|
||||
|
||||
/// \defgroup FC_ Flow chart flags
|
||||
/// Passed as 'flags' parameter to qflow_chart_t
|
||||
//@{
|
||||
#define FC_PRINT 0x0001 ///< print names (used only by display_flow_chart())
|
||||
#define FC_NOEXT 0x0002 ///< do not compute external blocks. Use this to prevent jumps leaving the
|
||||
///< function from appearing in the flow chart. Unless specified, the
|
||||
///< targets of those outgoing jumps will be present in the flow
|
||||
///< chart under the form of one-instruction blocks
|
||||
#define FC_RESERVED 0x0004 // former FC_PREDS
|
||||
#define FC_APPND 0x0008 ///< multirange flowchart (set by append_to_flowchart)
|
||||
#define FC_CHKBREAK 0x0010 ///< build_qflow_chart() may be aborted by user
|
||||
#define FC_CALL_ENDS 0x0020 ///< call instructions terminate basic blocks
|
||||
#define FC_NOPREDS 0x0040 ///< do not compute predecessor lists
|
||||
//@}
|
||||
|
||||
/// A flow chart for a function, or a set of address ranges
|
||||
class qflow_chart_t : public cancellable_graph_t
|
||||
{
|
||||
public:
|
||||
typedef qvector<qbasic_block_t> blocks_t;
|
||||
DECLARE_HELPER(friend)
|
||||
qstring title;
|
||||
range_t bounds; ///< overall bounds of the qflow_chart_t instance
|
||||
func_t *pfn = nullptr; ///< the function this instance was built upon
|
||||
int flags = 0; ///< flags. See \ref FC_
|
||||
blocks_t blocks; ///< basic blocks
|
||||
int nproper = 0; ///< number of basic blocks belonging to the specified range
|
||||
|
||||
idaapi qflow_chart_t(void) {}
|
||||
idaapi qflow_chart_t(const char *_title, func_t *_pfn, ea_t _ea1, ea_t _ea2, int _flags)
|
||||
: title(_title), bounds(_ea1, _ea2), pfn(_pfn), flags(_flags)
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
DEFINE_VIRTUAL_DTOR(qflow_chart_t)
|
||||
void idaapi create(const char *_title, func_t *_pfn, ea_t _ea1, ea_t _ea2, int _flags)
|
||||
{
|
||||
title = _title;
|
||||
pfn = _pfn;
|
||||
bounds = range_t(_ea1, _ea2);
|
||||
flags = _flags;
|
||||
refresh();
|
||||
}
|
||||
void idaapi create(const char *_title, const rangevec_t &ranges, int _flags)
|
||||
{
|
||||
title = _title;
|
||||
flags = _flags;
|
||||
create_multirange_qflow_chart(*this, ranges);
|
||||
}
|
||||
void idaapi append_to_flowchart(ea_t ea1, ea_t ea2) { ::append_to_flowchart(*this, ea1, ea2); }
|
||||
void idaapi refresh(void) { create_qflow_chart(*this); }
|
||||
fc_block_type_t calc_block_type(size_t blknum) const
|
||||
{ return fc_calc_block_type(*this, blknum); }
|
||||
bool is_ret_block(size_t blknum) const { return ::is_ret_block(calc_block_type(blknum)); }
|
||||
bool is_noret_block(size_t blknum) const { return ::is_noret_block(calc_block_type(blknum)); }
|
||||
virtual void idaapi print_node_attributes(FILE * /*fp*/, int /*n*/) const override {}
|
||||
virtual int idaapi nsucc(int node) const override { return int(blocks[node].succ.size()); }
|
||||
virtual int idaapi npred(int node) const override { return int(blocks[node].pred.size()); }
|
||||
virtual int idaapi succ(int node, int i) const override { return blocks[node].succ[i]; }
|
||||
virtual int idaapi pred(int node, int i) const override { return blocks[node].pred[i]; }
|
||||
virtual char *idaapi get_node_label(char * /*buf*/, int /*bufsize*/, int /*n*/) const override { return NULL; }
|
||||
virtual int idaapi size(void) const override { return int(blocks.size()); }
|
||||
bool idaapi print_names(void) const { return (flags & FC_PRINT) != 0; }
|
||||
};
|
||||
|
||||
#endif // __GDLDRAW_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _HELP_H
|
||||
#define _HELP_H
|
||||
|
||||
typedef int help_t; /* Help messages are referred by ints */
|
||||
|
||||
// Get pointer to message text by its message id
|
||||
// The message texts are read from ida.hlp at the beginning
|
||||
// Returns: pointer to message text (NULL is never returned by IDA)
|
||||
|
||||
idaman THREAD_SAFE const char *ida_export itext(help_t msg_id);
|
||||
|
||||
#ifdef __KERNWIN_HPP
|
||||
GCC_DIAG_OFF(format-nonliteral);
|
||||
NORETURN inline void Err(help_t format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
verror(itext(format), va);
|
||||
// NOTREACHED
|
||||
}
|
||||
|
||||
inline void Warn(help_t format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
vwarning(itext(format), va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
inline void Info(help_t format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
vinfo(itext(format), va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
inline int Message(help_t format,...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
int nbytes = vmsg(itext(format), va);
|
||||
va_end(va);
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
inline int vask_yn(int deflt, help_t format, va_list va)
|
||||
{
|
||||
return vask_yn(deflt, itext(format), va);
|
||||
}
|
||||
|
||||
inline int ask_yn(int deflt, help_t format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
int code = vask_yn(deflt, itext(format), va);
|
||||
va_end(va);
|
||||
return code;
|
||||
}
|
||||
GCC_DIAG_ON(format-nonliteral);
|
||||
#endif
|
||||
|
||||
#ifndef NO_OBSOLETE_FUNCS
|
||||
#endif
|
||||
|
||||
#endif /* _HELP_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,146 +0,0 @@
|
||||
#ifndef IDA_HIGHLIGHTER
|
||||
#define IDA_HIGHLIGHTER
|
||||
|
||||
#include <expr.hpp>
|
||||
|
||||
idaman void ida_export code_highlight_block(void *context, highlighter_cbs_t *highlighter_cbs, const qstring &text);
|
||||
|
||||
// returns the length of text to colorize
|
||||
// negative values may have special meaning in the future.
|
||||
typedef ssize_t idaapi external_colorizer_t(syntax_highlight_style *color, const char *str);
|
||||
typedef qvector<external_colorizer_t *> external_colorizers_t;
|
||||
|
||||
// returns true if identifier is colorized
|
||||
typedef bool idaapi external_ident_colorizer_t(syntax_highlight_style *color, const qstring &ident);
|
||||
typedef qvector<external_ident_colorizer_t *> external_ident_colorizers_t;
|
||||
|
||||
|
||||
#define MLTCMTMASK 0xF
|
||||
#define PREPROC_FLAG 0x10
|
||||
|
||||
// Common implementation of syntax_highlighter_t used in different parts of IDA
|
||||
//-V:ida_syntax_highlighter_t:730 not all members of a class are initialized inside the constructor
|
||||
struct ida_syntax_highlighter_t : syntax_highlighter_t
|
||||
{
|
||||
protected:
|
||||
// keys of keywords_t point to memory from keyword_strings.
|
||||
// once allocated, this buffer won't be moved in the memory.
|
||||
qstrvec_t keyword_memory;
|
||||
// helper class so that keywords_t gets sorted by string contents, not pointer values.
|
||||
struct plain_char_ptr_t
|
||||
{
|
||||
const char *ptr;
|
||||
plain_char_ptr_t(const char *p = NULL) : ptr(p) {}
|
||||
bool operator <(const plain_char_ptr_t &r) const
|
||||
{
|
||||
return strcmp(ptr, r.ptr) < 0;
|
||||
}
|
||||
bool operator ==(const plain_char_ptr_t &r) const
|
||||
{
|
||||
return strcmp(ptr, r.ptr) == 0;
|
||||
}
|
||||
};
|
||||
struct multicmt_t
|
||||
{
|
||||
qstring open_multicmt;
|
||||
qstring close_multicmt;
|
||||
multicmt_t() {}
|
||||
multicmt_t(const char *_open_multicmt, const char *_close_multicmt) :
|
||||
open_multicmt(_open_multicmt),
|
||||
close_multicmt(_close_multicmt)
|
||||
{}
|
||||
};
|
||||
|
||||
// typedef std::map<plain_char_ptr_t, syntax_highlight_style> keywords_t;
|
||||
struct keywords_style_t
|
||||
{
|
||||
qvector<plain_char_ptr_t> keywords;
|
||||
syntax_highlight_style style;
|
||||
};
|
||||
typedef qvector<keywords_style_t> keywords_t;
|
||||
//typedef qvector<plain_char_ptr_t> keywords_t;
|
||||
typedef qvector<multicmt_t> multicmtvec_t;
|
||||
keywords_t keywords;
|
||||
|
||||
qstring open_cmt; // string that opens a regular line comment
|
||||
multicmtvec_t multicmts;
|
||||
char literal_closer; // either close_strconst or close_chrconst for the current literal
|
||||
|
||||
// color mappings
|
||||
syntax_highlight_style text_color = HF_DEFAULT;
|
||||
syntax_highlight_style comment_color = HF_COMMENT;
|
||||
syntax_highlight_style string_color = HF_STRING;
|
||||
syntax_highlight_style preprocessor_color = HF_PREPROC;
|
||||
|
||||
external_colorizers_t external_colorizers;
|
||||
external_ident_colorizers_t external_ident_colorizers;
|
||||
|
||||
// work variables
|
||||
const char *input; // pointer to the start of the input buffer
|
||||
const char *pending; // pointer in the input buffer
|
||||
syntax_highlight_style style = HF_DEFAULT; // current highlighting style
|
||||
|
||||
bool pending_nonspaces_present(const char *end)
|
||||
{
|
||||
for ( const char *p = pending; p != end; ++p )
|
||||
if ( !qisspace(*p) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *parse_literal_const(highlighter_cbs_t *highlighter_cbs, const char *ptr, char literal_closer);
|
||||
void flush_output(highlighter_cbs_t *highlighter_cbs, const char *ptr, syntax_highlight_style style);
|
||||
void handle_cmt(highlighter_cbs_t *highlighter_cbs, int mcmt_idx, const char **ptr);
|
||||
void handle_preproc(highlighter_cbs_t *highlighter_cbs, const char **ptr);
|
||||
|
||||
public:
|
||||
// if any of the following features is not needed, just zero them out:
|
||||
char open_strconst; // character that opens a string constant
|
||||
char close_strconst; // character that closes a string constant
|
||||
char open_chrconst; // character that closes a character constant
|
||||
char close_chrconst; // character that opens a character constant
|
||||
char escape_char; // backslash
|
||||
char preprocessor_char; // #
|
||||
|
||||
ida_syntax_highlighter_t() : syntax_highlighter_t(&code_highlight_block) {}
|
||||
|
||||
void highlight_block_ex(highlighter_cbs_t *highlighter_cbs, const qstring &text);
|
||||
void add_external_colorizer(external_colorizer_t *th) { external_colorizers.push_back(th); }
|
||||
void add_external_ident_colorizer(external_ident_colorizer_t *th) { external_ident_colorizers.push_back(th); }
|
||||
void set_open_cmt(const char *begin) { open_cmt = begin; }
|
||||
void add_multi_line_comment(const char *begin, const char *end)
|
||||
{
|
||||
multicmt_t &mcmt = multicmts.push_back();
|
||||
mcmt.open_multicmt = begin;
|
||||
mcmt.close_multicmt = end;
|
||||
}
|
||||
|
||||
void add_keywords(const char *kwstr, syntax_highlight_style _style)
|
||||
{
|
||||
char *ctx;
|
||||
// in order not to allocate every keyword separately, we allocate the whole
|
||||
// kwstr string at once and will just store pointers to it in the map.
|
||||
qstring &mem = keyword_memory.push_back();
|
||||
mem = kwstr;
|
||||
keywords_style_t *pair_p = NULL;
|
||||
for ( int i = 0; i < keywords.size(); i++ )
|
||||
{
|
||||
if ( keywords[i].style == _style )
|
||||
{
|
||||
pair_p = &keywords[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( pair_p == NULL )
|
||||
{
|
||||
keywords_style_t &pair = keywords.push_back();
|
||||
pair_p = &pair;
|
||||
pair_p->style = _style;
|
||||
}
|
||||
for ( char *kw = qstrtok(mem.begin(), "|", &ctx); kw != NULL; kw = qstrtok(NULL, "|", &ctx) )
|
||||
pair_p->keywords.push_back(kw);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif //IDA_HIGHLIGHTER
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,306 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA)
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
|
||||
* Floating Point Number Libary.
|
||||
* Copyright (c) 1995-2006 by Iouri Kharon.
|
||||
* E-mail: yjh@styx.cabel.net
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _IEEE_H_
|
||||
#define _IEEE_H_
|
||||
|
||||
/*! \file ieee.h
|
||||
|
||||
\brief IEEE floating point functions
|
||||
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// Number of 16 bit words in ::eNE
|
||||
#define IEEE_NE 6
|
||||
|
||||
/// Number of 16 bit words in ::eNI
|
||||
#define IEEE_NI (IEEE_NE+3)
|
||||
|
||||
//==========================================================================
|
||||
/// Array offset to exponent
|
||||
#define IEEE_E 1
|
||||
|
||||
/// Array offset to high guard word
|
||||
#define IEEE_M 2
|
||||
|
||||
/// The exponent of 1.0
|
||||
#define IEEE_EXONE (0x3fff)
|
||||
|
||||
//===================================================================
|
||||
/// External x type format
|
||||
typedef uint16 eNE[IEEE_NE];
|
||||
|
||||
/// Internal format:
|
||||
/// - 0 : sign (0/1)
|
||||
/// - 1 : exponent (based of #IEEE_EXONE). If exp = 0, value = 0.
|
||||
/// - 2 : high word of mantissa (always zero after normalize)
|
||||
typedef uint16 eNI[IEEE_NI];
|
||||
|
||||
/// Exponent in eNE for NaN and Inf
|
||||
#define E_SPECIAL_EXP 0x7fff
|
||||
|
||||
/// 0.0
|
||||
extern const eNE ieee_ezero;
|
||||
#define EZERO { 0, 0000000,0000000,0000000,0000000,0000000 }
|
||||
|
||||
/// 1.0
|
||||
extern const eNE ieee_eone;
|
||||
#define EONE { 0, 0000000,0000000,0000000,0100000,0x3fff }
|
||||
|
||||
/// 2.0
|
||||
extern const eNE ieee_etwo;
|
||||
|
||||
/// 32.0
|
||||
extern const eNE ieee_e32;
|
||||
|
||||
/// 6.93147180559945309417232121458176568075500134360255E-1
|
||||
extern const eNE ieee_elog2;
|
||||
|
||||
/// 1.41421356237309504880168872420969807856967187537695E0
|
||||
extern const eNE ieee_esqrt2;
|
||||
|
||||
/// 2/sqrt(PI) = 1.12837916709551257389615890312154517168810125865800E0
|
||||
extern const eNE ieee_eoneopi;
|
||||
|
||||
/// 3.14159265358979323846264338327950288419716939937511E0
|
||||
extern const eNE ieee_epi;
|
||||
|
||||
/// 5.7721566490153286060651209008240243104215933593992E-1
|
||||
extern const eNE ieee_eeul;
|
||||
|
||||
|
||||
/// Clear (zero-out) the given value
|
||||
|
||||
inline void ecleaz(eNI x) { memset(x, 0, sizeof(eNI)); }
|
||||
|
||||
|
||||
/// Move eNI => eNE
|
||||
|
||||
idaman THREAD_SAFE void ida_export emovo(const eNI a, eNE b);
|
||||
|
||||
|
||||
/// Move eNE => eNI
|
||||
|
||||
idaman THREAD_SAFE void ida_export emovi(const eNE a, eNI b);
|
||||
|
||||
|
||||
/// Shift NI format up (+) or down
|
||||
|
||||
idaman THREAD_SAFE int ida_export eshift(eNI x, int sc);
|
||||
|
||||
|
||||
/// Normalize and round off.
|
||||
/// \param s the internal format number to be rounded
|
||||
/// \param lost indicates whether or not the number is exact.
|
||||
/// this is the so-called sticky bit.
|
||||
/// \param subflg indicates whether the number was obtained
|
||||
/// by a subtraction operation. In that case if lost is nonzero
|
||||
/// then the number is slightly smaller than indicated.
|
||||
/// \param exp the biased exponent, which may be negative.
|
||||
/// the exponent field of "s" is ignored but is replaced by
|
||||
/// "exp" as adjusted by normalization and rounding.
|
||||
/// \param rndbase if 0 => is the rounding control.
|
||||
/// else is processor defined base (rndprc)
|
||||
/// \return success
|
||||
|
||||
idaman THREAD_SAFE int ida_export emdnorm(eNI s, int lost, int subflg, int32 exp, int rndbase);
|
||||
|
||||
|
||||
/// \defgroup REAL_ERROR_ Floating point/IEEE Conversion codes
|
||||
/// Return values for and processor_t::realcvt_t request
|
||||
//@{
|
||||
#define REAL_ERROR_FORMAT -1 ///< not supported format for current .idp
|
||||
#define REAL_ERROR_RANGE -2 ///< number too big (small) for store (mem NOT modified)
|
||||
#define REAL_ERROR_BADDATA -3 ///< illegal real data for load (IEEE data not filled)
|
||||
//@}
|
||||
//
|
||||
/// \name Prototypes
|
||||
/// IDP module event prototype -- should be implemented in idp
|
||||
//@{
|
||||
|
||||
/// Floating point conversion function: implemented by \ph{realcvt}.
|
||||
/// \param m pointer to data
|
||||
/// \param e internal IEEE format data
|
||||
/// \param swt operation:
|
||||
/// - 000: load trunc. float (DEC ^F) 2 bytes (m->e)
|
||||
/// - 001: load float 4 bytes (m->e)
|
||||
/// - 003: load double 8 bytes (m->e)
|
||||
/// - 004: load long double 10 bytes (m->e)
|
||||
/// - 005: load long double 12 bytes (m->e)
|
||||
/// - 010: store trunc. float (DEC ^F) 2 bytes (e->m)
|
||||
/// - 011: store float 4 bytes (e->m)
|
||||
/// - 013: store double 8 bytes (e->m)
|
||||
/// - 014: store long double 10 bytes (e->m)
|
||||
/// - 015: store long double 12 bytes (e->m)
|
||||
/// \retval 0 ok
|
||||
/// \retval \ref REAL_ERROR_ on error
|
||||
|
||||
int idaapi realcvt(void *m, eNE e, uint16 swt);
|
||||
|
||||
|
||||
/// Little endian
|
||||
|
||||
int l_realcvt(void *m, eNE e, uint16 swt);
|
||||
|
||||
|
||||
/// Big endian
|
||||
|
||||
int b_realcvt(void *m, eNE e, uint16 swt);
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/// Standard IEEE 754 floating point conversion function.
|
||||
/// See comment for realcvt().
|
||||
|
||||
idaman THREAD_SAFE int ida_export ieee_realcvt(void *m, eNE e, uint16 swt);
|
||||
|
||||
|
||||
/// \name Misc arithmetic/conversion functions
|
||||
/// \retval 0 ok
|
||||
/// \retval 1 overfloat / underfloat
|
||||
/// \retval 2 illegal data (asctoreal())
|
||||
/// \retval 3 divide by 0 (ediv())
|
||||
/// \retval 4 too big for integer (eetol())
|
||||
//@{
|
||||
|
||||
/// IEEE to ascii string.
|
||||
/// \param mode broken down into:
|
||||
/// - low byte: number of digits after '.'
|
||||
/// - second byte: FPNUM_LENGTH
|
||||
/// - third byte: FPNUM_DIGITS
|
||||
|
||||
idaman THREAD_SAFE void ida_export realtoasc(char *buf, size_t bufsize, const eNE x, uint mode);
|
||||
|
||||
|
||||
/// ascii string to IEEE
|
||||
|
||||
idaman THREAD_SAFE int ida_export asctoreal(const char **sss, eNE y);
|
||||
|
||||
|
||||
/// long to IEEE
|
||||
|
||||
idaman THREAD_SAFE void ida_export eltoe(sval_t l, eNE e);
|
||||
|
||||
|
||||
/// int64 to IEEE
|
||||
|
||||
idaman THREAD_SAFE void ida_export eltoe64(int64 l, eNE e);
|
||||
|
||||
|
||||
/// uint64 to IEEE
|
||||
|
||||
idaman THREAD_SAFE void ida_export eltoe64u(uint64 l, eNE e);
|
||||
|
||||
|
||||
/// IEEE to long (+-0.5 if flg)
|
||||
|
||||
idaman THREAD_SAFE int ida_export eetol(sval_t *l, const eNE a, bool roundflg);
|
||||
|
||||
|
||||
/// IEEE to long (+-0.5 if flg)
|
||||
|
||||
idaman THREAD_SAFE int ida_export eetol64(int64 *l, const eNE a, bool roundflg);
|
||||
|
||||
|
||||
/// IEEE to ulong (+-0.5 if flg)
|
||||
|
||||
idaman THREAD_SAFE int ida_export eetol64u(uint64 *l, const eNE a, bool roundflg);
|
||||
|
||||
|
||||
/// b = a*(2**pwr2)
|
||||
|
||||
idaman THREAD_SAFE int ida_export eldexp(const eNE a, int32 pwr2, eNE b);
|
||||
|
||||
|
||||
/// if(!subflg) c = a + b,
|
||||
/// else c = a - b
|
||||
|
||||
idaman THREAD_SAFE int ida_export eadd(const eNE a, const eNE b, eNE c, int subflg);
|
||||
|
||||
|
||||
/// c = a * b
|
||||
|
||||
idaman THREAD_SAFE int ida_export emul(const eNE a, const eNE b, eNE c);
|
||||
|
||||
|
||||
/// c = a / b
|
||||
|
||||
idaman THREAD_SAFE int ida_export ediv(const eNE a, const eNE b, eNE c);
|
||||
|
||||
|
||||
// predefined functions
|
||||
/// \cond
|
||||
void eclear(eNE a);
|
||||
void emov(eNE a, eNE b);
|
||||
void eabs(eNE x);
|
||||
int esign(eNE x);
|
||||
/// \endcond
|
||||
|
||||
/// x = 0
|
||||
#define eclear(a) memset(a, 0, sizeof(eNE))
|
||||
|
||||
/// b = a
|
||||
#define emov(a, b) memcpy(b, a, sizeof(eNE))
|
||||
|
||||
/// x = |x|
|
||||
#define eabs(x) (x[IEEE_NE-1] &= 0x7fff)
|
||||
|
||||
#ifdef __cplusplus
|
||||
/// x = -x
|
||||
inline void eneg(eNE x)
|
||||
{
|
||||
if ( x[IEEE_NE-1] != 0 )
|
||||
x[IEEE_NE-1] ^= 0x8000;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// x < 0 ?
|
||||
/// \note non standard answer is returned
|
||||
#define esign(x) (x[IEEE_NE-1] & 0x8000)
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/// Comparison.
|
||||
/// \retval 0 if a = b
|
||||
/// \retval 1 if a > b
|
||||
/// \retval -1 if a < b
|
||||
|
||||
idaman THREAD_SAFE int ida_export ecmp(const eNE a, const eNE b);
|
||||
|
||||
|
||||
/// Check for NaN/Inf
|
||||
enum fpvalue_kind_t
|
||||
{
|
||||
FPV_BADARG, ///< wrong value of max_exp
|
||||
FPV_NORM, ///< regular value
|
||||
FPV_NAN, ///< NaN
|
||||
FPV_PINF, ///< positive infinity
|
||||
FPV_NINF, ///< negative infinity
|
||||
};
|
||||
|
||||
/// \name max_exp values
|
||||
/// common values for max_exp (for IEEE floating point values)
|
||||
//@{
|
||||
const uint32
|
||||
MAXEXP_FLOAT = 0x80,
|
||||
MAXEXP_DOUBLE = 0x400,
|
||||
MAXEXP_LNGDBL = 0x4000;
|
||||
//@}
|
||||
|
||||
|
||||
/// See ::fpvalue_kind_t
|
||||
|
||||
idaman THREAD_SAFE fpvalue_kind_t ida_export get_fpvalue_kind(const eNE a, uint16 reserved = 0);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,824 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __JUMPTABLE_HPP
|
||||
#define __JUMPTABLE_HPP
|
||||
|
||||
#include <pro.h>
|
||||
#include <ua.hpp> // op_t
|
||||
#include <nalt.hpp> // switch_info_t, jumptable_info_t
|
||||
#include <map>
|
||||
|
||||
// Class to check for a jump table sequence.
|
||||
// This class should be used in preference to the hard encoding of jump table sequences
|
||||
// because it allows for:
|
||||
// - instruction rescheduling
|
||||
// - intermingling the jump sequence with other instructions
|
||||
// - sequence variants
|
||||
//
|
||||
// For this class:
|
||||
// all instructions of the sequence are numbered starting from the last instruction.
|
||||
// The last instruction has the number 0.
|
||||
// The instruction before the last instruciton has the number 1, etc.
|
||||
// There is a virtual function jpiN() for each instruction of the sequence
|
||||
// These functions return true if 'insn' is filled with the required instruction
|
||||
//
|
||||
// The comparison is made in the match() function:
|
||||
//
|
||||
// ea points to the last instruction of the sequence (instruction #0)
|
||||
//
|
||||
// the 'depends' array contains dependencies between the instructions of the sequence.
|
||||
// For example:
|
||||
// ARM thumb LDRH switch
|
||||
// 7 SUB Ra, #minv (optional)
|
||||
// 6 CMP Ra, #size
|
||||
// 5 BCS defea
|
||||
// 4 ADR Rb, jt
|
||||
// 3 ADD Rb, Rb, Ra
|
||||
// 2 LDRH Rb, [Rb,Ra]
|
||||
// 1 LSL Rb, Rb, #1
|
||||
// 0 ADD PC, Rb
|
||||
// In this sequence, instruction #0 depends on the value of Rb which is produced
|
||||
// by the instruction #1. So, the instruction #0 depends on #1. Therefore, depends[0]
|
||||
// will contain '1' as its element.
|
||||
// The instruction #3 depends on 2 registers: Ra and Rb, or in other words,
|
||||
// it depends on the instructions #4 and #6. Therefore, depends[2] will contain { 4, 6 }
|
||||
// Maximum 4 dependencies per instruction are allowed.
|
||||
//
|
||||
// FIXME
|
||||
// The 'roots' array contains the first instruction of the dependency chains.
|
||||
// In our case we can say that there are 2 dependency chains:
|
||||
// 0 -> 1 -> 2 -> 3 -> 4
|
||||
// -> 6 -> 7
|
||||
// 5 -> 6
|
||||
// Therefore the roots array will consist of {1, 5}.
|
||||
// 0 denotes the end of the chain and cannot be the root of a dependency chain
|
||||
// Usually 1 is a root of any jump sequence.
|
||||
//
|
||||
// The dependency array allows for checking for optimized sequences of instructions.
|
||||
// If 2 instructions are not dependent on each other, they may appear in any order.
|
||||
// (for example, the instruction #4 and the instruction sequence #5-6-7 may appear
|
||||
// in any order because they do not depend on each other)
|
||||
// Also any other instructions not modifying the register values may appear between
|
||||
// the instructions of the sequence (due to the instruction rescheduling performed
|
||||
// by the compiler).
|
||||
//
|
||||
// Provision for optional instructions:
|
||||
// The presence of an optional instruction in the sequence (like #7) is signalled
|
||||
// by a negative number of the dependency in the 'depends' array.
|
||||
//
|
||||
// Provision for variable instructions:
|
||||
// In some cases several variants of the same instructions may be supported.
|
||||
// For example, the instruction #5 might be BCS as well as BGE. It is the job of
|
||||
// the jpi5() function to check for all variants.
|
||||
//
|
||||
// In order to use the 'jump_pattern_t' class you should derive another class from it
|
||||
// and define the jpiN() virtual functions.
|
||||
// Then you have to define the 'depends' and 'roots' arrays and call the match()
|
||||
// function.
|
||||
// If you processor contains instructions who modify registers in peculiar ways
|
||||
// you might want to override the check_spoiled() function.
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Macro to declare implementation of methods of jump_pattern_t
|
||||
class jump_pattern_t;
|
||||
// tracked registers
|
||||
// We use the 'size' term to denote the number of bits involved in the insn.
|
||||
// E.g. an operand of type dt_byte has 8-bit size.
|
||||
// We store the current size (the number of used bits) in the DTYPE field
|
||||
// of the 'op_t' structure. It may differ from the size of operand in the
|
||||
// insn. See the comment for set_moved().
|
||||
// We extend the 'op_dtype_t' type by some negative constants to denote
|
||||
// sizes from 2 to 7 bits.
|
||||
typedef qvector<op_t> tracked_regs_t;
|
||||
#define DECLARE_JUMP_PATTERN_HELPERS(decl)\
|
||||
decl void ida_export check_spoiled_jpt(const jump_pattern_t *_this, tracked_regs_t *_regs); \
|
||||
decl bool ida_export match_jpt(jump_pattern_t *_this);\
|
||||
decl bool ida_export same_value_jpt(jump_pattern_t *_this, const op_t &op, int r_i);\
|
||||
decl void ida_export combine_regs_jpt(jump_pattern_t *_this, tracked_regs_t *dst, const tracked_regs_t &src, ea_t ea);\
|
||||
decl void ida_export mark_switch_insns_jpt(const jump_pattern_t *_this, int last, int);\
|
||||
decl bool ida_export set_moved_jpt(const jump_pattern_t *_this, const op_t &dst, const op_t &src, tracked_regs_t &_regs, op_dtype_t real_dst_dtype, op_dtype_t real_src_dtype);
|
||||
|
||||
DECLARE_JUMP_PATTERN_HELPERS(idaman)
|
||||
|
||||
class jump_pattern_t
|
||||
{
|
||||
protected:
|
||||
// 32-bit operand generates a 32-bit result, zero- or sign-extended to a
|
||||
// 64-bit result. This flag may be overwritten in processor modules.
|
||||
// For example:
|
||||
// ARM: MOV W8, #0x3C will clear the upper 32 bits of X8,
|
||||
// PC : mov eax, 3Ch will clear the upper 32 bits of rax
|
||||
bool modifying_r32_spoils_r64;
|
||||
|
||||
public:
|
||||
typedef bool (jump_pattern_t::*check_insn_t)(void);
|
||||
inline jump_pattern_t(
|
||||
switch_info_t *si, // may be NULL
|
||||
const char (*depends)[4],
|
||||
int last_reg);
|
||||
|
||||
insn_t insn; // current instruction
|
||||
switch_info_t *si; // answers will be here
|
||||
|
||||
enum
|
||||
{
|
||||
NINS = 16, // the maximum length of the sequence
|
||||
INS_MASK = 0x0F,
|
||||
};
|
||||
ea_t eas[NINS];
|
||||
bool skip[NINS]; // do not check the Nth insn if skip[N] is true
|
||||
int non_spoiled_reg; // if non_spoiled_reg was spoiled then we stop
|
||||
// matching
|
||||
check_insn_t check[NINS];
|
||||
// this is the hidden return value of the jpiN() methods. If it is set and
|
||||
// jpiN() returned 'true' then we stop processing the dependency chain. If
|
||||
// it is set and jpiN() returned 'false' then we stop checking the insns
|
||||
// in the current basic block and we are switching to the next one (and we
|
||||
// fail if there is no such block).
|
||||
bool stop_matching;
|
||||
// this flag can be analyzed by jpiN(). It means that the current insn is
|
||||
// in the linear flow from the previous insn. It is always 'true' if the
|
||||
// insn has JPT_NEAR flag.
|
||||
bool in_linear_flow;
|
||||
// this address can be analyzed by jpiN(). It means the end of the current
|
||||
// block. It may help if we want to check in-block jumps.
|
||||
ea_t block_end;
|
||||
|
||||
#define JPT_OPT 0x10 // the dependent insn might be missing
|
||||
#define JPT_NEAR 0x20 // the dependent insn must be in the linear flow
|
||||
|
||||
const char (*depends)[4]; // instruction, on which we depend, and
|
||||
// additional JPT_... flags
|
||||
|
||||
// mark swith instructions to be ignored by the decompiler
|
||||
// do not mark the indirect jmp (eas[0]) as ignored
|
||||
// it will be used to recognize switch idioms
|
||||
// unmark NLOWCASE insns after LAST (in the case of SWI_HXNOLOWCASE flag)
|
||||
void mark_switch_insns(int last = NINS - 1, int nlowcase = 0) const
|
||||
{
|
||||
mark_switch_insns_jpt(this, last, nlowcase);
|
||||
}
|
||||
|
||||
// for fragmented switch idioms, cmp/jbe might be located in a separate
|
||||
// fragment. we must not mark these instructions as part of the switch
|
||||
// idiom because doing so would spoil the program logic for the decompiler
|
||||
// and make the switch operator unreachable. the following vector keeps
|
||||
// addresses of all instructions which must not be marked. this vector is
|
||||
// maintained by derived classes.
|
||||
eavec_t remote_code;
|
||||
// extra insns used to calculate values (discovered by find_op_value)
|
||||
eavec_t extra_insn_eas;
|
||||
// tracked registers
|
||||
tracked_regs_t regs;
|
||||
|
||||
// handle a possible delay slot situation
|
||||
// while walking backwards in the execution flow
|
||||
// if <branch> is false and <ea> is in a delay
|
||||
// slot of a branch likely instruction
|
||||
// then set <ea> to the branch instruction
|
||||
// (=annul the delay slot)
|
||||
// if <branch> is true and the instruction at <ea>
|
||||
// has a delay slot then set <ea> to the delay slot
|
||||
// (=execute the delay slot)
|
||||
virtual void process_delay_slot(ea_t &/*ea*/, bool /*branch*/) const {}
|
||||
|
||||
// an artificial register to track the address of the conditional jump
|
||||
// .value - condition
|
||||
// .addr - address of the conditional jump
|
||||
// .specval - address of the default case
|
||||
// the derived class can use .reg to track the condition register
|
||||
enum
|
||||
{
|
||||
o_condjump = 99,
|
||||
cc_inc_ncases = 0x01, // increment ncases
|
||||
cc_check_max_ncases = 0x02, // comparison with the maximum value
|
||||
};
|
||||
|
||||
// compare supported operands
|
||||
virtual bool equal_ops(const op_t &x, const op_t &y) const
|
||||
{
|
||||
if ( x.type != y.type )
|
||||
return false;
|
||||
switch ( x.type )
|
||||
{
|
||||
case o_void:
|
||||
// consider spoiled values as not equal
|
||||
return false;
|
||||
case o_reg:
|
||||
// ignore difference in the data size of registers
|
||||
return x.reg == y.reg;
|
||||
case o_condjump:
|
||||
// we do not track the condition flags
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if the instruction `insn' is a move one,
|
||||
// there is no need check spoiled registers in this case
|
||||
virtual bool handle_mov(tracked_regs_t & /*_regs*/ )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// does the instruction `insn' spoil `_regs' ?
|
||||
virtual void check_spoiled(tracked_regs_t *_regs) const
|
||||
{
|
||||
check_spoiled_jpt(this, _regs);
|
||||
}
|
||||
// some binaries use the following pattern
|
||||
// xor eax, eax | mov al, cl
|
||||
// so we can extend dtype of the operand from dt_byte to dt_dword
|
||||
virtual op_dtype_t extend_dtype(const op_t &op) const
|
||||
{
|
||||
return op.dtype; // do not extend
|
||||
}
|
||||
|
||||
// these methods are not virtual and should be used in processor
|
||||
// module only
|
||||
inline void track(int reg, int r_i, op_dtype_t dtype);
|
||||
inline void trackop(const op_t &op, int r_i);
|
||||
inline bool is_spoiled(int r_i) { return regs[r_i].type == o_void; }
|
||||
inline bool is_equal(int reg, int r_i, op_dtype_t dtype);
|
||||
inline bool is_equal(const op_t &op, int r_i);
|
||||
inline bool same_value(const op_t &op, int r_i);
|
||||
|
||||
virtual bool jpi0(void) = 0;
|
||||
virtual bool jpi1(void) { return false; }
|
||||
virtual bool jpi2(void) { return false; }
|
||||
virtual bool jpi3(void) { return false; }
|
||||
virtual bool jpi4(void) { return false; }
|
||||
virtual bool jpi5(void) { return false; }
|
||||
virtual bool jpi6(void) { return false; }
|
||||
virtual bool jpi7(void) { return false; }
|
||||
virtual bool jpi8(void) { return false; }
|
||||
virtual bool jpi9(void) { return false; }
|
||||
virtual bool jpia(void) { return false; }
|
||||
virtual bool jpib(void) { return false; }
|
||||
virtual bool jpic(void) { return false; }
|
||||
virtual bool jpid(void) { return false; }
|
||||
virtual bool jpie(void) { return false; }
|
||||
virtual bool jpif(void) { return false; }
|
||||
// jpi<n> will be called if pre_jpi returns true
|
||||
virtual bool pre_jpi(int /*n*/) { return true; }
|
||||
|
||||
bool match(const insn_t &_insn) { insn = _insn; return match_jpt(this); }
|
||||
|
||||
// remove compiler warnings -- class with virtual functions MUST have virtual destructor
|
||||
virtual ~jump_pattern_t() {}
|
||||
|
||||
// helpers for mov instruction tracing (see methods handle_mov(),
|
||||
// check_spoiled() above)
|
||||
inline static void set_spoiled(tracked_regs_t *_regs);
|
||||
inline void set_spoiled(tracked_regs_t *_regs, const op_t &op) const;
|
||||
// track 'mov' insn: dst <- src
|
||||
// it returns 'true' if insn changes any of the tracked registers
|
||||
// REAL_DST_DTYPE is the size that will be changed in the DST operand by
|
||||
// the insn. It can be greater than the operand size because some insns
|
||||
// clear the upper bits. For example:
|
||||
// xor eax, eax | mov ax, cx REAL_DST_DTYPE is 32
|
||||
// xor bh, bh | mov bl, cl REAL_DST_DTYPE is 16
|
||||
// Extending of the 32-bit register to 64 bits is performed automatically
|
||||
// based on the modifying_r32_spoils_r64 flag.
|
||||
// REAL_SRC_DTYPE is the size that will be used in the SRC operand by the
|
||||
// insn. It can be less than the operand size. For example:
|
||||
// ARM: AND W8, W8, #0xFF will use 8 bits of X8,
|
||||
// PC : cwde will use 16 bits of rax.
|
||||
bool set_moved(
|
||||
const op_t &dst,
|
||||
const op_t &src,
|
||||
tracked_regs_t &_regs,
|
||||
op_dtype_t real_dst_dtype = dt_void,
|
||||
op_dtype_t real_src_dtype = dt_void) const
|
||||
{
|
||||
return set_moved_jpt(this, dst, src, _regs, real_dst_dtype, real_src_dtype);
|
||||
}
|
||||
// calculate state of registers before a conditional jump <ea> as the
|
||||
// combination of states of each branch
|
||||
void combine_regs(
|
||||
tracked_regs_t *dst,
|
||||
const tracked_regs_t &src,
|
||||
ea_t ea)
|
||||
{
|
||||
combine_regs_jpt(this, dst, src, ea);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool match_tree();
|
||||
bool follow_tree(ea_t ea, int n);
|
||||
bool same_value_impl(const op_t &op, int r_i);
|
||||
|
||||
inline bool equal_ops_dtype(const op_t &op, const op_t ®) const;
|
||||
static inline bool is_narrower(op_dtype_t dt1, op_dtype_t dt2);
|
||||
enum
|
||||
{
|
||||
dt_7bit = 255,
|
||||
dt_6bit = 254,
|
||||
dt_5bit = 253,
|
||||
dt_4bit = 252,
|
||||
dt_3bit = 251,
|
||||
dt_2bit = 250,
|
||||
};
|
||||
static inline int get_dtype_nbits(op_dtype_t dtype);
|
||||
|
||||
// helper for check_spoiled()
|
||||
// TODO introduce new virtual methods spoils() and spoils_flags() and
|
||||
// replace check_spoiled() by non-virtual method
|
||||
inline void check_spoiled_not_reg(
|
||||
tracked_regs_t *_regs,
|
||||
uint maxop = UA_MAXOP) const;
|
||||
|
||||
DECLARE_JUMP_PATTERN_HELPERS(friend)
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// kinds of jump tables
|
||||
enum { JT_NONE = 0, JT_SWITCH, JT_CALL };
|
||||
// It returns a nonzero JT_... kind if it found a jump pattern. This kind is
|
||||
// passed to the check_table() function.
|
||||
typedef int is_pattern_t(switch_info_t *si, const insn_t &insn, procmod_t *procmod);
|
||||
// It returns a refined kind. For example, JT_NONE if the found jump pattern
|
||||
// is not a switch, or JT_CALL if it is a call of a func from an array
|
||||
typedef int table_checker_t(
|
||||
switch_info_t *si,
|
||||
ea_t jump_ea,
|
||||
int is_pattern_res,
|
||||
procmod_t *pm);
|
||||
// check a flat 32/16/8 bit jump table -- the most common case
|
||||
idaman int ida_export check_flat_jump_table(
|
||||
switch_info_t *si,
|
||||
ea_t jump_ea,
|
||||
int is_pattern_res = JT_SWITCH);
|
||||
|
||||
// This function finds a switch. It calls functions from the PATTERNS
|
||||
// array in turn until the first one returns a nonzero value.
|
||||
// If a suitable pattern is found, it calls check_table() for the final
|
||||
// check, passing a nonzero result code of the 'is_pattern_t' function.
|
||||
// If the CHECK_TABLE parameter is NULL then check_flat_jump_table() is
|
||||
// called.
|
||||
// NAME is used for a debug output.
|
||||
// It returns 'false' if INSN is not a switch or it is a call of a func from
|
||||
// an array. In the latter case it defines this array.
|
||||
idaman bool ida_export check_for_table_jump(
|
||||
switch_info_t *si,
|
||||
const insn_t &insn,
|
||||
is_pattern_t *const patterns[],
|
||||
size_t qty,
|
||||
table_checker_t *check_table = NULL,
|
||||
const char *name = NULL);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// sometimes the size of the jump table is misdetected
|
||||
// check if any of the would-be targets point into the table
|
||||
// and if so, truncate it
|
||||
// if 'ignore_refs' is false, also stop at first data reference
|
||||
idaman void ida_export trim_jtable(
|
||||
switch_info_t *si,
|
||||
ea_t jump_ea,
|
||||
bool ignore_refs = false);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// this function find the size of the jump table for indirect switches
|
||||
// (indirect switches have the values table which contains indexes into
|
||||
// the jump table)
|
||||
// in: si->ncases has the size of the values table
|
||||
// out: si->jcases is initialized
|
||||
idaman bool ida_export find_jtable_size(switch_info_t *si);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// get default jump address from the jump table.
|
||||
// This method can be used only for a sparse nonindirect switch with default
|
||||
// case in the jump table.
|
||||
idaman ea_t ida_export find_defjump_from_table(
|
||||
ea_t jump_ea,
|
||||
const switch_info_t &si);
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// iterate instructions in the backward execution flow
|
||||
//lint -esym(1512,backward_flow_iterator_t*) destructor is not virtual
|
||||
template<class State,class Ctrl>
|
||||
// State: default constructor, operator=
|
||||
// Ctrl: combine_regs(State *, const State& ,ea_t)
|
||||
// process_delay_slot(ea_t &/*ea*/, bool /*branch*/)
|
||||
struct backward_flow_iterator_t
|
||||
{
|
||||
public:
|
||||
ea_t cur_ea; // current address
|
||||
State ®s; // current state of the tracked registers
|
||||
Ctrl &ctrl; // to combine state
|
||||
bool only_near; // should we follow only the linear flow?
|
||||
uint max_insn_cnt;
|
||||
|
||||
protected:
|
||||
//lint --e{958} padding is required
|
||||
func_t *pfn; // to check bounds
|
||||
const segment_t *seg;
|
||||
ea_t start_ea;
|
||||
ea_t cur_end; // end of current basic block
|
||||
uint insn_cnt;
|
||||
// visited basic blocks:
|
||||
// key_type - start of the block, mapped_type - end of the block;
|
||||
typedef std::map<ea_t, ea_t> visited_t;
|
||||
visited_t visited;
|
||||
// waiting basic blocks:
|
||||
// key_type - end of the block, mapped_type - state at the end;
|
||||
struct state_t
|
||||
{
|
||||
State regs;
|
||||
uint insn_cnt;
|
||||
state_t() : regs(), insn_cnt(UINT_MAX) {}
|
||||
};
|
||||
typedef std::map<ea_t, state_t> waiting_t;
|
||||
waiting_t waiting;
|
||||
|
||||
public:
|
||||
backward_flow_iterator_t(
|
||||
ea_t start_ea_,
|
||||
State &start_regs,
|
||||
Ctrl &ctrl_,
|
||||
bool only_near_,
|
||||
uint max_insn_cnt_ = 0)
|
||||
: cur_ea(start_ea_),
|
||||
regs(start_regs),
|
||||
ctrl(ctrl_),
|
||||
only_near(only_near_),
|
||||
max_insn_cnt(max_insn_cnt_),
|
||||
pfn(NULL),
|
||||
seg(NULL),
|
||||
start_ea(start_ea_),
|
||||
cur_end(BADADDR),
|
||||
insn_cnt(0),
|
||||
visited(),
|
||||
waiting()
|
||||
{
|
||||
// to check bounds
|
||||
pfn = get_func(start_ea);
|
||||
if ( pfn == NULL )
|
||||
{
|
||||
seg = getseg(start_ea);
|
||||
QASSERT(10183, seg != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// fl_U : no previous instruction (start of a function or a cycle,
|
||||
// or non linear flow if ONLY_NEAR is true),
|
||||
// fl_F : got previous instruction by linear flow,
|
||||
// fl_JF: got previous instruction by jump;
|
||||
inline cref_t prev_insn();
|
||||
// stop iterating the current basic block, switch to the lowest waiting
|
||||
// block
|
||||
inline cref_t skip_block();
|
||||
|
||||
inline ea_t get_cur_end() const
|
||||
{
|
||||
return cur_end == BADADDR ? cur_ea : cur_end;
|
||||
}
|
||||
|
||||
protected:
|
||||
// find visited basic block containing the address
|
||||
// it returns the pointer to the address of the block end or NULL
|
||||
inline ea_t *find_visited(ea_t ea);
|
||||
// get the lowest to start_ea waiting block
|
||||
inline cref_t get_waiting();
|
||||
// combine insn counter - count the shortest path
|
||||
static inline void combine_insn_cnt(uint *dst, uint src)
|
||||
{
|
||||
if ( src < *dst )
|
||||
*dst = src;
|
||||
}
|
||||
|
||||
bool check_bounds() const
|
||||
{
|
||||
if ( pfn != NULL )
|
||||
return func_contains(pfn, cur_ea);
|
||||
return seg->contains(cur_ea);
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// simple backward flow iterator
|
||||
struct no_regs_t {};
|
||||
struct simple_bfi_t
|
||||
: public backward_flow_iterator_t<no_regs_t, simple_bfi_t>
|
||||
{
|
||||
typedef backward_flow_iterator_t<no_regs_t, simple_bfi_t> base_t;
|
||||
|
||||
protected:
|
||||
no_regs_t regs_;
|
||||
|
||||
public:
|
||||
simple_bfi_t(ea_t ea)
|
||||
: base_t(ea, regs_, *this, false) {}
|
||||
static void combine_regs(no_regs_t *, const no_regs_t &, ea_t) {}
|
||||
static void process_delay_slot(ea_t &, bool) {}
|
||||
};
|
||||
|
||||
|
||||
//======================================================================
|
||||
// inline implementation
|
||||
//----------------------------------------------------------------------
|
||||
//-V:jump_pattern_t:730 not all members of a class are initialized inside the constructor
|
||||
inline jump_pattern_t::jump_pattern_t(
|
||||
switch_info_t *_si,
|
||||
const char (*_depends)[4],
|
||||
int last_reg)
|
||||
: modifying_r32_spoils_r64(true),
|
||||
si(_si),
|
||||
non_spoiled_reg(-1),
|
||||
in_linear_flow(false),
|
||||
depends(_depends),
|
||||
regs()
|
||||
{
|
||||
if ( si != NULL )
|
||||
si->clear();
|
||||
regs.resize(last_reg + 1);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
inline bool jump_pattern_t::equal_ops_dtype(
|
||||
const op_t &op,
|
||||
const op_t ®) const
|
||||
{
|
||||
if ( !equal_ops(op, reg) )
|
||||
return false;
|
||||
// operand should be wider than a tracked register
|
||||
// e.g. after 'cmp cl, imm' we cannot use cx
|
||||
if ( !is_narrower(op.dtype, reg.dtype) )
|
||||
return true;
|
||||
// we believe that dword is widened to qword
|
||||
if ( modifying_r32_spoils_r64 && op.dtype == dt_dword )
|
||||
return true;
|
||||
// try to extend
|
||||
if ( !is_narrower(extend_dtype(op), reg.dtype) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// return true if size1 is narrow than size2
|
||||
inline bool jump_pattern_t::is_narrower(op_dtype_t dt1, op_dtype_t dt2)
|
||||
{
|
||||
if ( dt1 < dt_2bit )
|
||||
return dt2 < dt_2bit && dt1 < dt2;
|
||||
else
|
||||
return dt2 < dt_2bit || dt1 < dt2;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
inline int jump_pattern_t::get_dtype_nbits(op_dtype_t dtype)
|
||||
{
|
||||
switch ( dtype )
|
||||
{
|
||||
case dt_byte: return 8;
|
||||
case dt_word: return 16;
|
||||
case dt_dword: return 32;
|
||||
case dt_qword: return 64;
|
||||
case dt_7bit: return 7;
|
||||
case dt_6bit: return 6;
|
||||
case dt_5bit: return 5;
|
||||
case dt_4bit: return 4;
|
||||
case dt_3bit: return 3;
|
||||
case dt_2bit: return 2;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
inline void jump_pattern_t::check_spoiled_not_reg(
|
||||
tracked_regs_t *_regs,
|
||||
uint maxop) const
|
||||
{
|
||||
uint32 feature = insn.get_canon_feature(PH);
|
||||
if ( feature == 0 )
|
||||
return;
|
||||
for ( uint i = 0; i < maxop; ++i )
|
||||
{
|
||||
if ( has_cf_chg(feature, i)
|
||||
&& insn.ops[i].type != o_void
|
||||
&& insn.ops[i].type != o_reg )
|
||||
{
|
||||
set_spoiled(_regs, insn.ops[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
inline void jump_pattern_t::track(int reg, int r_i, op_dtype_t dtype)
|
||||
{
|
||||
regs[r_i].type = o_reg;
|
||||
regs[r_i].reg = reg;
|
||||
regs[r_i].dtype = dtype;
|
||||
}
|
||||
inline void jump_pattern_t::trackop(const op_t &op, int r_i)
|
||||
{
|
||||
regs[r_i] = op;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
inline bool jump_pattern_t::is_equal(int reg, int r_i, op_dtype_t dtype)
|
||||
{
|
||||
op_t op;
|
||||
op.type = o_reg;
|
||||
op.reg = reg;
|
||||
op.dtype = dtype;
|
||||
return is_equal(op, r_i);
|
||||
}
|
||||
inline bool jump_pattern_t::is_equal(const op_t &op, int r_i)
|
||||
{
|
||||
if ( regs[r_i].type == o_void )
|
||||
{
|
||||
// there is no reason to continue match
|
||||
stop_matching = true;
|
||||
return false;
|
||||
}
|
||||
return equal_ops_dtype(op, regs[r_i]);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
inline bool jump_pattern_t::same_value(const op_t &op, int r_i)
|
||||
{
|
||||
return same_value_jpt(this, op, r_i);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
inline void jump_pattern_t::set_spoiled(tracked_regs_t *__regs)
|
||||
{
|
||||
tracked_regs_t &_regs = *__regs;
|
||||
// spoil all registers
|
||||
for ( size_t i = 0; i < _regs.size(); ++i )
|
||||
_regs[i].type = o_void;
|
||||
}
|
||||
inline void jump_pattern_t::set_spoiled(tracked_regs_t *__regs, const op_t &op) const
|
||||
{
|
||||
tracked_regs_t &_regs = *__regs;
|
||||
for ( size_t i = 0; i < _regs.size(); ++i )
|
||||
if ( equal_ops(_regs[i], op) )
|
||||
_regs[i].type = o_void; // spoil register
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// find the previous instruction in code flow
|
||||
// take into account branches and potential delay slots
|
||||
template<class State,class Ctrl>
|
||||
inline cref_t backward_flow_iterator_t<State,Ctrl>::prev_insn()
|
||||
{
|
||||
size_t refcnt = 0;
|
||||
// check visited basic block
|
||||
ea_t *visited_end = find_visited(cur_ea);
|
||||
if ( visited_end == NULL )
|
||||
{
|
||||
// analyze references to the current address
|
||||
flags_t F = get_flags(cur_ea);
|
||||
if ( is_flow(F) )
|
||||
++refcnt;
|
||||
if ( has_xref(F) && !is_func(F) ) // do not count jumps to function
|
||||
{
|
||||
xrefblk_t xb;
|
||||
for ( bool ok = xb.first_to(cur_ea, XREF_FAR);
|
||||
ok && xb.iscode;
|
||||
ok = xb.next_to() )
|
||||
{
|
||||
// count only xrefs from jumps
|
||||
if ( xb.type == fl_JF || xb.type == fl_JN )
|
||||
{
|
||||
if ( only_near )
|
||||
{
|
||||
if ( refcnt > 0 )
|
||||
return fl_U;
|
||||
// do not consider the flow through another switch as linear
|
||||
if ( (get_flags(xb.from) & FF_JUMP) != 0 )
|
||||
return fl_U;
|
||||
}
|
||||
++refcnt;
|
||||
ea_t ea = xb.from;
|
||||
ctrl.process_delay_slot(ea, true);
|
||||
// ignore jumps from already visited blocks
|
||||
if ( find_visited(ea) != NULL )
|
||||
continue;
|
||||
// add basic block to the waiting set (combine state of the
|
||||
// tracked registers at the jump source)
|
||||
state_t &src_state = waiting[ea];
|
||||
ctrl.combine_regs(&src_state.regs, regs, ea);
|
||||
combine_insn_cnt(&src_state.insn_cnt, insn_cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( cur_end == BADADDR )
|
||||
cur_end = cur_ea;
|
||||
|
||||
// try ordinary flow
|
||||
if ( is_flow(F) )
|
||||
{
|
||||
ea_t prev_ea = prev_not_tail(cur_ea);
|
||||
if ( prev_ea != BADADDR )
|
||||
{
|
||||
cur_ea = prev_ea;
|
||||
if ( check_bounds()
|
||||
&& (max_insn_cnt == 0 || insn_cnt < max_insn_cnt) )
|
||||
{
|
||||
++insn_cnt;
|
||||
// remove reached waiting basic block
|
||||
typename waiting_t::iterator w = waiting.find(cur_ea);
|
||||
if ( w != waiting.end() )
|
||||
{
|
||||
ctrl.combine_regs(®s, w->second.regs, cur_ea);
|
||||
combine_insn_cnt(&insn_cnt, w->second.insn_cnt);
|
||||
waiting.erase(w);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctrl.process_delay_slot(cur_ea, false);
|
||||
}
|
||||
return fl_F;
|
||||
}
|
||||
}
|
||||
// choose another branch
|
||||
}
|
||||
|
||||
// save block [cur_ea, cur_end] as visited
|
||||
visited[cur_ea] = cur_end;
|
||||
}
|
||||
else if ( cur_end != BADADDR )
|
||||
{
|
||||
// reach visited basic block => extend it
|
||||
*visited_end = cur_end;
|
||||
}
|
||||
|
||||
// get the lowest waiting block
|
||||
cref_t ret = get_waiting();
|
||||
// consider one xref as a linear flow
|
||||
if ( ret == fl_JF && refcnt == 1 && waiting.empty() )
|
||||
ret = fl_F;
|
||||
return ret;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
template<class State,class Ctrl>
|
||||
inline cref_t backward_flow_iterator_t<State,Ctrl>::skip_block()
|
||||
{
|
||||
// check visited basic block
|
||||
ea_t *visited_end = find_visited(cur_ea);
|
||||
if ( visited_end == NULL )
|
||||
{
|
||||
if ( cur_end == BADADDR )
|
||||
cur_end = cur_ea;
|
||||
// save block [cur_ea, cur_end] as visited
|
||||
visited[cur_ea] = cur_end;
|
||||
}
|
||||
else if ( cur_end != BADADDR )
|
||||
{
|
||||
// reach visited basic block => extend it
|
||||
*visited_end = cur_end;
|
||||
}
|
||||
|
||||
// get the lowest waiting block
|
||||
return get_waiting();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
template<class State,class Ctrl>
|
||||
inline cref_t backward_flow_iterator_t<State,Ctrl>::get_waiting()
|
||||
{
|
||||
while ( !waiting.empty() )
|
||||
{
|
||||
typename waiting_t::iterator w = waiting.upper_bound(start_ea);
|
||||
if ( w != waiting.begin() )
|
||||
--w;
|
||||
cur_ea = w->first;
|
||||
if ( check_bounds() )
|
||||
{
|
||||
cur_end = BADADDR;
|
||||
regs = w->second.regs;
|
||||
insn_cnt = w->second.insn_cnt;
|
||||
waiting.erase(w);
|
||||
return fl_JF;
|
||||
}
|
||||
waiting.erase(w);
|
||||
}
|
||||
return fl_U;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
template<class State,class Ctrl>
|
||||
inline ea_t *backward_flow_iterator_t<State,Ctrl>::find_visited(ea_t ea)
|
||||
{
|
||||
visited_t::iterator v = visited.upper_bound(ea);
|
||||
// assert: v == visited.end() || v->first > ea
|
||||
if ( v == visited.begin() )
|
||||
return NULL;
|
||||
--v;
|
||||
// assert: v->first <= ea
|
||||
if ( ea > v->second )
|
||||
return NULL;
|
||||
return &v->second;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,353 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LEX_HPP
|
||||
#define LEX_HPP
|
||||
|
||||
/*! \file lex.hpp
|
||||
|
||||
\brief Tools for parsing C-like input
|
||||
|
||||
Functions in this file use objects of opaque type lexer_t.
|
||||
To create a lexer_t instance, use create_lexer().
|
||||
*/
|
||||
|
||||
typedef ushort lxtype; ///< see \ref lx_
|
||||
|
||||
/// \defgroup lx_ Parser token types
|
||||
/// See token_t::type
|
||||
/// \note All separators have their ASCII codes as lxtype
|
||||
//@{
|
||||
const lxtype
|
||||
lx_end = 1, ///< no more tokens
|
||||
lx_ident = 2, ///< ident
|
||||
lx_number = 3, ///< long constant
|
||||
lx_string = 4, ///< string constant (token_t.chr != 0 => unicode string)
|
||||
lx_char = 5, ///< char constant
|
||||
lx_typename = 6, ///< user-defined type
|
||||
lx_float = 7, ///< IEEE floating point constant
|
||||
lx_int64 = 8, ///< int64 constant
|
||||
lx_key = 128; ///< keywords start. All keys are lx_key + keynum. \n
|
||||
///< Two-char separators are: (c1 + (c2 << 8)). \n
|
||||
///< Three-char separators:
|
||||
///< - "<<=" = ('<' + ('<'<<8)) + '='
|
||||
///< - ">>=" = ('>' + ('>'<<8)) + '='
|
||||
//@}
|
||||
|
||||
|
||||
/// Parser token
|
||||
struct token_t
|
||||
{
|
||||
qstring str; ///< idents & strings
|
||||
lxtype type; ///< see \ref lx_
|
||||
sval_t num; ///< long & char constants
|
||||
union
|
||||
{
|
||||
bool unicode; ///< (::lx_string: != 0 => unicode string)
|
||||
bool is_unsigned; ///< (::lx_number, ::lx_int64: != 0 => unsigned value)
|
||||
};
|
||||
union
|
||||
{
|
||||
ushort fnum[6]; ///< floating point constant
|
||||
int64 i64; ///< ::lx_int64
|
||||
};
|
||||
token_t() : type(0), num(0), unicode(false)
|
||||
{
|
||||
// init maximum member of union
|
||||
memset(&fnum[0], 0x00, sizeof(fnum));
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(token_t);
|
||||
|
||||
class lexer_t; // lexical analyzer, opaque structure
|
||||
|
||||
|
||||
/// Preprocessor callback for unknown tokens.
|
||||
/// Will be called when preprocessor calculates the value of #if expression.
|
||||
|
||||
typedef error_t lx_resolver_t(lexer_t *lx, void *ud, token_t *curtok, sval_t *res);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
// Conversion from 'type1 ' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
|
||||
// We want this sign-extension to happen, since it comes mostly from HANDLEs.
|
||||
// (see https://msdn.microsoft.com/en-us/library/ms235307.aspx )
|
||||
#pragma warning(disable:4826)
|
||||
#endif
|
||||
|
||||
/// Preprocessor cast
|
||||
struct cast_t
|
||||
{
|
||||
bool is_unsigned;
|
||||
int size;
|
||||
|
||||
cast_t()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
void reset(void)
|
||||
{
|
||||
is_unsigned = false;
|
||||
size = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct lex_value_t
|
||||
{
|
||||
bool is_unsigned;
|
||||
union
|
||||
{
|
||||
int64 val;
|
||||
uint64 uval;
|
||||
};
|
||||
|
||||
lex_value_t()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
void set(const lex_value_t &v)
|
||||
{
|
||||
set_val(v.val, v.is_unsigned);
|
||||
}
|
||||
void reset(void)
|
||||
{
|
||||
set_val(0, true);
|
||||
}
|
||||
void set_val(int64 v, bool _is_unsigned)
|
||||
{
|
||||
is_unsigned = _is_unsigned;
|
||||
val = v;
|
||||
}
|
||||
|
||||
uint64 get_uval(void) const
|
||||
{
|
||||
return val;
|
||||
}
|
||||
int64 get_val(void) const
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
bool is_zero(void) const
|
||||
{
|
||||
return get_val() == 0;
|
||||
}
|
||||
|
||||
void perform_cast(const cast_t &cast);
|
||||
|
||||
void unary_minus(const lex_value_t &v);
|
||||
void unary_plus(const lex_value_t &v);
|
||||
void unary_not(const lex_value_t &v);
|
||||
void bitwise_not(const lex_value_t &v);
|
||||
|
||||
void mul(const lex_value_t &v);
|
||||
void div(const lex_value_t &v);
|
||||
void mod(const lex_value_t &v);
|
||||
void add(const lex_value_t &v);
|
||||
void sub(const lex_value_t &v);
|
||||
|
||||
void shift_right(const lex_value_t &v);
|
||||
void shift_left(const lex_value_t &v);
|
||||
void bitwise_and(const lex_value_t &v);
|
||||
void bitwise_xor(const lex_value_t &v);
|
||||
void bitwise_or(const lex_value_t &v);
|
||||
void logical_and(const lex_value_t &v);
|
||||
void logical_or(const lex_value_t &v);
|
||||
|
||||
void cmpge(const lex_value_t &v);
|
||||
void cmple(const lex_value_t &v);
|
||||
void cmplt(const lex_value_t &v);
|
||||
void cmpgt(const lex_value_t &v);
|
||||
void cmpeq(const lex_value_t &v);
|
||||
void cmpneq(const lex_value_t &v);
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
/// Preprocessor callbacks for casts.
|
||||
/// Will be called when preprocessor calculates the value of #if expression.
|
||||
|
||||
typedef error_t lx_parse_cast_t(lexer_t *lx, cast_t *cast, token_t *ct);
|
||||
|
||||
/// Preprocessor callback.
|
||||
/// It will be called for each input line.
|
||||
/// \return an error code (0-ok)
|
||||
|
||||
typedef int idaapi lx_preprocessor_cb(void *ud, const char *fname, int nl, const char *line);
|
||||
|
||||
|
||||
/// Callback for #pragma directives.
|
||||
/// \return an error code (0-ok)
|
||||
|
||||
typedef int idaapi lx_pragma_cb(void *ud, const char *line);
|
||||
|
||||
|
||||
/// Callback for #warning directives.
|
||||
/// \return an error code (0-ok)
|
||||
|
||||
typedef int idaapi lx_warning_cb(void *ud, const char *line);
|
||||
|
||||
|
||||
/// Callback for #define directives
|
||||
/// \return an error code (0-ok)
|
||||
|
||||
typedef int idaapi lx_macro_cb(
|
||||
void *ud,
|
||||
const char *name,
|
||||
const char *body,
|
||||
int nargs,
|
||||
bool isfunc,
|
||||
bool is_new_macro);
|
||||
|
||||
|
||||
/// Callback for #undef directives
|
||||
/// \return an error code (0-ok)
|
||||
|
||||
typedef int idaapi lx_undef_cb(void *ud, const char *name);
|
||||
|
||||
|
||||
/// Create new lexical analyzer and set its keyword table.
|
||||
/// If keys==NULL, then set the default C keyword table
|
||||
|
||||
idaman lexer_t *ida_export create_lexer(
|
||||
const char *const *keys,
|
||||
size_t size,
|
||||
void *ud=NULL);
|
||||
|
||||
|
||||
/// Destroy a lexical analyzer
|
||||
|
||||
idaman void ida_export destroy_lexer(lexer_t *lx);
|
||||
|
||||
|
||||
/// Define a macro
|
||||
|
||||
idaman error_t ida_export lex_define_macro(
|
||||
lexer_t *lx,
|
||||
const char *macro,
|
||||
const char *body,
|
||||
int nargs=0,
|
||||
bool isfunc=false);
|
||||
|
||||
/// Undefine a macro
|
||||
|
||||
idaman void ida_export lex_undefine_macro(
|
||||
lexer_t *lx,
|
||||
const char *macro);
|
||||
|
||||
/// Set lexer options.
|
||||
/// \param options \ref LXOPT_
|
||||
/// \return the old options
|
||||
|
||||
idaman int ida_export lex_set_options(lexer_t *lx, int options);
|
||||
|
||||
/// \defgroup LXOPT_ Lexer options
|
||||
/// Passed as 'options' parameter to lex_set_options().
|
||||
/// By default all options are on.
|
||||
//@{
|
||||
#define LXOPT_PARSE_FLOATS 0x0001 ///< enable floating point constants
|
||||
#define LXOPT_REQ_SEPARATOR 0x0002 ///< require a separator between a number and an ident or a character/string constant or dot
|
||||
#define LXOPT_NOCASE_FILES 0x0004 ///< case-insensitive file search
|
||||
#define LXOPT_C99_CONSTANTS 0x0008 ///< the size and sign of constants depend on the value itself and the 'U', 'L', and 'LL'
|
||||
///< modifier suffixes. otherwise the constant is always considered as signed and the size
|
||||
///< depends only on the number of bytes in the value
|
||||
//@}
|
||||
|
||||
|
||||
/// Get next token
|
||||
/// \param p_lnnum line number where the token starts
|
||||
|
||||
idaman error_t ida_export lex_get_token(lexer_t *lx, token_t *t);
|
||||
idaman error_t ida_export lex_get_token2(lexer_t *lx, token_t *t, int32 *p_lnnum);
|
||||
|
||||
|
||||
/// Enumerate all macros.
|
||||
/// Do so until 'cb' returns non-zero.
|
||||
|
||||
idaman int ida_export lex_enum_macros(
|
||||
const lexer_t *lx,
|
||||
int idaapi cb(const char *name, const char *body, int nargs, bool isfunc, void *ud),
|
||||
void *ud=NULL);
|
||||
|
||||
|
||||
/// Debug: get text representation of token
|
||||
|
||||
idaman const char *ida_export lex_print_token(qstring *buf, const token_t *t);
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// \name String oriented functions
|
||||
//@{
|
||||
|
||||
/// Set the input line and the macro table.
|
||||
/// if macros==NULL, the macro table will not be changed.
|
||||
|
||||
idaman error_t ida_export lex_init_string(
|
||||
lexer_t *lx,
|
||||
const char *line,
|
||||
void *macros=NULL);
|
||||
//@}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// \name File oriented functions
|
||||
//@{
|
||||
|
||||
/// Initialization: file may be NULL.
|
||||
/// Also see lex_term_file().
|
||||
|
||||
idaman error_t ida_export lex_init_file(lexer_t *lx, const char *file);
|
||||
|
||||
|
||||
/// Error handling.
|
||||
/// if level > 0, then return information about the enclosing file which
|
||||
/// included the current one.
|
||||
|
||||
idaman const char *ida_export lex_get_file_line(
|
||||
lexer_t *lx,
|
||||
int32 *linenum,
|
||||
const char **lineptr,
|
||||
int level=0);
|
||||
|
||||
|
||||
/// Termination: also see lex_init_file()
|
||||
|
||||
idaman void ida_export lex_term_file(lexer_t *lx, bool del_macros);
|
||||
//@}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// \name Token stack
|
||||
//@{
|
||||
typedef qstack<token_t> tokenstack_t; ///< see get_token(), unget_token()
|
||||
|
||||
|
||||
/// Retrieve token from a stack or lexer.
|
||||
/// If buf is not empty then get the token on top of the stack.
|
||||
/// If buf is empty then gen the next token from the lexer.
|
||||
/// \return success
|
||||
|
||||
inline bool get_token(token_t *t, lexer_t *lx, tokenstack_t &buf)
|
||||
{
|
||||
if ( !buf.empty() )
|
||||
*t = buf.pop();
|
||||
else if ( lex_get_token(lx, t) != eOk )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Push a token back onto the token stack
|
||||
|
||||
inline void unget_token(const token_t &t, tokenstack_t &buf)
|
||||
{
|
||||
buf.push(t);
|
||||
}
|
||||
//@}
|
||||
|
||||
|
||||
#endif // LEX_HPP
|
||||
@@ -1,595 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINES_HPP
|
||||
#define _LINES_HPP
|
||||
|
||||
#include <ida.hpp>
|
||||
|
||||
/*! \file lines.hpp
|
||||
|
||||
\brief High level functions that deal with the generation
|
||||
of the disassembled text lines.
|
||||
|
||||
This file also contains definitions for the syntax highlighting.
|
||||
|
||||
Finally there are functions that deal with anterior/posterior
|
||||
user-defined lines.
|
||||
*/
|
||||
|
||||
struct range_t;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// C O L O R D E F I N I T I O N S
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/// \defgroup color_def Color definitions
|
||||
///
|
||||
/// Here we describe the structure of embedded escape sequences used to
|
||||
/// implement syntax highlighting.
|
||||
///
|
||||
/// IDP module should insert appropriate escape characters into the
|
||||
/// output lines as necessary. This approach allows to create an IDP
|
||||
/// module without the syntax highlighting too - just don't use
|
||||
/// escape sequences.
|
||||
///
|
||||
/// A typical color sequence looks like this:
|
||||
///
|
||||
/// #COLOR_ON COLOR_xxx text #COLOR_OFF COLOR_xxx
|
||||
///
|
||||
/// The first 2 items turn color 'xxx' on, then the text follows,
|
||||
/// and the color is turned off by two last items.
|
||||
///
|
||||
/// For the convenience we've defined a set of macro definitions
|
||||
/// and functions to deal with colors.
|
||||
//@{
|
||||
|
||||
/// \defgroup color_esc Color escape characters
|
||||
/// Initiate/Terminate a color tag
|
||||
//@{
|
||||
#define COLOR_ON '\1' ///< Escape character (ON).
|
||||
///< Followed by a color code (::color_t).
|
||||
#define COLOR_OFF '\2' ///< Escape character (OFF).
|
||||
///< Followed by a color code (::color_t).
|
||||
#define COLOR_ESC '\3' ///< Escape character (Quote next character).
|
||||
///< This is needed to output '\1' and '\2'
|
||||
///< characters.
|
||||
#define COLOR_INV '\4' ///< Escape character (Inverse foreground and background colors).
|
||||
///< This escape character has no corresponding #COLOR_OFF.
|
||||
///< Its action continues until the next #COLOR_INV or end of line.
|
||||
|
||||
#define SCOLOR_ON "\1" ///< Escape character (ON)
|
||||
#define SCOLOR_OFF "\2" ///< Escape character (OFF)
|
||||
#define SCOLOR_ESC "\3" ///< Escape character (Quote next character)
|
||||
#define SCOLOR_INV "\4" ///< Escape character (Inverse colors)
|
||||
|
||||
/// Is the given char a color escape character?
|
||||
inline THREAD_SAFE bool requires_color_esc(char c) { return c >= COLOR_ON && c <= COLOR_INV; }
|
||||
//@}
|
||||
|
||||
typedef uchar color_t; ///< color tag - see \ref COLOR_
|
||||
/// \defgroup COLOR_ Color tags
|
||||
/// Specify a color for a syntax item
|
||||
//@{
|
||||
const color_t
|
||||
COLOR_DEFAULT = 0x01, ///< Default
|
||||
COLOR_REGCMT = 0x02, ///< Regular comment
|
||||
COLOR_RPTCMT = 0x03, ///< Repeatable comment (comment defined somewhere else)
|
||||
COLOR_AUTOCMT = 0x04, ///< Automatic comment
|
||||
COLOR_INSN = 0x05, ///< Instruction
|
||||
COLOR_DATNAME = 0x06, ///< Dummy Data Name
|
||||
COLOR_DNAME = 0x07, ///< Regular Data Name
|
||||
COLOR_DEMNAME = 0x08, ///< Demangled Name
|
||||
COLOR_SYMBOL = 0x09, ///< Punctuation
|
||||
COLOR_CHAR = 0x0A, ///< Char constant in instruction
|
||||
COLOR_STRING = 0x0B, ///< String constant in instruction
|
||||
COLOR_NUMBER = 0x0C, ///< Numeric constant in instruction
|
||||
COLOR_VOIDOP = 0x0D, ///< Void operand
|
||||
COLOR_CREF = 0x0E, ///< Code reference
|
||||
COLOR_DREF = 0x0F, ///< Data reference
|
||||
COLOR_CREFTAIL = 0x10, ///< Code reference to tail byte
|
||||
COLOR_DREFTAIL = 0x11, ///< Data reference to tail byte
|
||||
COLOR_ERROR = 0x12, ///< Error or problem
|
||||
COLOR_PREFIX = 0x13, ///< Line prefix
|
||||
COLOR_BINPREF = 0x14, ///< Binary line prefix bytes
|
||||
COLOR_EXTRA = 0x15, ///< Extra line
|
||||
COLOR_ALTOP = 0x16, ///< Alternative operand
|
||||
COLOR_HIDNAME = 0x17, ///< Hidden name
|
||||
COLOR_LIBNAME = 0x18, ///< Library function name
|
||||
COLOR_LOCNAME = 0x19, ///< Local variable name
|
||||
COLOR_CODNAME = 0x1A, ///< Dummy code name
|
||||
COLOR_ASMDIR = 0x1B, ///< Assembler directive
|
||||
COLOR_MACRO = 0x1C, ///< Macro
|
||||
COLOR_DSTR = 0x1D, ///< String constant in data directive
|
||||
COLOR_DCHAR = 0x1E, ///< Char constant in data directive
|
||||
COLOR_DNUM = 0x1F, ///< Numeric constant in data directive
|
||||
COLOR_KEYWORD = 0x20, ///< Keywords
|
||||
COLOR_REG = 0x21, ///< Register name
|
||||
COLOR_IMPNAME = 0x22, ///< Imported name
|
||||
COLOR_SEGNAME = 0x23, ///< Segment name
|
||||
COLOR_UNKNAME = 0x24, ///< Dummy unknown name
|
||||
COLOR_CNAME = 0x25, ///< Regular code name
|
||||
COLOR_UNAME = 0x26, ///< Regular unknown name
|
||||
COLOR_COLLAPSED= 0x27, ///< Collapsed line
|
||||
COLOR_FG_MAX = 0x28, ///< Max color number
|
||||
|
||||
// Fictive colors
|
||||
|
||||
COLOR_ADDR = COLOR_FG_MAX, ///< hidden address marks.
|
||||
///< the address is represented as 8digit
|
||||
///< hex number: 01234567.
|
||||
///< it doesn't have #COLOR_OFF pair.
|
||||
///< NB: for 64-bit IDA, the address is 16digit.
|
||||
|
||||
COLOR_OPND1 = COLOR_ADDR+1, ///< Instruction operand 1
|
||||
COLOR_OPND2 = COLOR_ADDR+2, ///< Instruction operand 2
|
||||
COLOR_OPND3 = COLOR_ADDR+3, ///< Instruction operand 3
|
||||
COLOR_OPND4 = COLOR_ADDR+4, ///< Instruction operand 4
|
||||
COLOR_OPND5 = COLOR_ADDR+5, ///< Instruction operand 5
|
||||
COLOR_OPND6 = COLOR_ADDR+6, ///< Instruction operand 6
|
||||
COLOR_OPND7 = COLOR_ADDR+7, ///< Instruction operand 7
|
||||
COLOR_OPND8 = COLOR_ADDR+8, ///< Instruction operand 8
|
||||
|
||||
|
||||
COLOR_RESERVED1= COLOR_ADDR+11,///< This tag is reserved for internal IDA use
|
||||
COLOR_LUMINA = COLOR_ADDR+12;///< Lumina-related, only for the navigation band
|
||||
//@}
|
||||
|
||||
/// Size of a tagged address (see ::COLOR_ADDR)
|
||||
#define COLOR_ADDR_SIZE (sizeof(ea_t)*2)
|
||||
|
||||
/// \defgroup SCOLOR_ Color string constants
|
||||
/// These definitions are used with the #COLSTR macro
|
||||
//@{
|
||||
#define SCOLOR_DEFAULT "\x01" ///< Default
|
||||
#define SCOLOR_REGCMT "\x02" ///< Regular comment
|
||||
#define SCOLOR_RPTCMT "\x03" ///< Repeatable comment (defined not here)
|
||||
#define SCOLOR_AUTOCMT "\x04" ///< Automatic comment
|
||||
#define SCOLOR_INSN "\x05" ///< Instruction
|
||||
#define SCOLOR_DATNAME "\x06" ///< Dummy Data Name
|
||||
#define SCOLOR_DNAME "\x07" ///< Regular Data Name
|
||||
#define SCOLOR_DEMNAME "\x08" ///< Demangled Name
|
||||
#define SCOLOR_SYMBOL "\x09" ///< Punctuation
|
||||
#define SCOLOR_CHAR "\x0A" ///< Char constant in instruction
|
||||
#define SCOLOR_STRING "\x0B" ///< String constant in instruction
|
||||
#define SCOLOR_NUMBER "\x0C" ///< Numeric constant in instruction
|
||||
#define SCOLOR_VOIDOP "\x0D" ///< Void operand
|
||||
#define SCOLOR_CREF "\x0E" ///< Code reference
|
||||
#define SCOLOR_DREF "\x0F" ///< Data reference
|
||||
#define SCOLOR_CREFTAIL "\x10" ///< Code reference to tail byte
|
||||
#define SCOLOR_DREFTAIL "\x11" ///< Data reference to tail byte
|
||||
#define SCOLOR_ERROR "\x12" ///< Error or problem
|
||||
#define SCOLOR_PREFIX "\x13" ///< Line prefix
|
||||
#define SCOLOR_BINPREF "\x14" ///< Binary line prefix bytes
|
||||
#define SCOLOR_EXTRA "\x15" ///< Extra line
|
||||
#define SCOLOR_ALTOP "\x16" ///< Alternative operand
|
||||
#define SCOLOR_HIDNAME "\x17" ///< Hidden name
|
||||
#define SCOLOR_LIBNAME "\x18" ///< Library function name
|
||||
#define SCOLOR_LOCNAME "\x19" ///< Local variable name
|
||||
#define SCOLOR_CODNAME "\x1A" ///< Dummy code name
|
||||
#define SCOLOR_ASMDIR "\x1B" ///< Assembler directive
|
||||
#define SCOLOR_MACRO "\x1C" ///< Macro
|
||||
#define SCOLOR_DSTR "\x1D" ///< String constant in data directive
|
||||
#define SCOLOR_DCHAR "\x1E" ///< Char constant in data directive
|
||||
#define SCOLOR_DNUM "\x1F" ///< Numeric constant in data directive
|
||||
#define SCOLOR_KEYWORD "\x20" ///< Keywords
|
||||
#define SCOLOR_REG "\x21" ///< Register name
|
||||
#define SCOLOR_IMPNAME "\x22" ///< Imported name
|
||||
#define SCOLOR_SEGNAME "\x23" ///< Segment name
|
||||
#define SCOLOR_UNKNAME "\x24" ///< Dummy unknown name
|
||||
#define SCOLOR_CNAME "\x25" ///< Regular code name
|
||||
#define SCOLOR_UNAME "\x26" ///< Regular unknown name
|
||||
#define SCOLOR_COLLAPSED "\x27" ///< Collapsed line
|
||||
#define SCOLOR_ADDR "\x28" ///< Hidden address mark
|
||||
//@}
|
||||
|
||||
//----------------- Line prefix colors --------------------------------------
|
||||
/// \defgroup COLOR_PFX Line prefix colors
|
||||
/// Note: line prefix colors are not used in processor modules
|
||||
//@{
|
||||
#define COLOR_DEFAULT 0x01 ///< Default
|
||||
#define COLOR_SELECTED 0x02 ///< Selected
|
||||
#define COLOR_LIBFUNC 0x03 ///< Library function
|
||||
#define COLOR_REGFUNC 0x04 ///< Regular function
|
||||
#define COLOR_CODE 0x05 ///< Single instruction
|
||||
#define COLOR_DATA 0x06 ///< Data bytes
|
||||
#define COLOR_UNKNOWN 0x07 ///< Unexplored byte
|
||||
#define COLOR_EXTERN 0x08 ///< External name definition segment
|
||||
#define COLOR_CURITEM 0x09 ///< Current item
|
||||
#define COLOR_CURLINE 0x0A ///< Current line
|
||||
#define COLOR_HIDLINE 0x0B ///< Hidden line
|
||||
#define COLOR_LUMFUNC 0x0C ///< Lumina function
|
||||
#define COLOR_BG_MAX 0x0D ///< Max color number
|
||||
|
||||
#define PALETTE_SIZE (COLOR_FG_MAX+COLOR_BG_MAX)
|
||||
//@}
|
||||
|
||||
|
||||
/// This macro is used to build colored string constants (e.g. for format strings)
|
||||
/// \param str string literal to surround with color tags
|
||||
/// \param tag one of SCOLOR_xxx constants
|
||||
#define COLSTR(str,tag) SCOLOR_ON tag str SCOLOR_OFF tag
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
/// \defgroup color_conv Convenience functions
|
||||
/// Higher level convenience functions are defined in ua.hpp.
|
||||
/// Please use the following functions only if functions from ua.hpp
|
||||
/// are not useful in your case.
|
||||
//@{
|
||||
|
||||
/// Insert an address mark into a string.
|
||||
/// \param buf pointer to the output buffer; the tag will be appended or inserted into it
|
||||
/// \param ea address to include
|
||||
/// \param ins if true, the tag will be inserted at the beginning of the buffer
|
||||
|
||||
idaman THREAD_SAFE void ida_export tag_addr(qstring *buf, ea_t ea, bool ins=false);
|
||||
|
||||
|
||||
/// Move pointer to a 'line' to 'cnt' positions right.
|
||||
/// Take into account escape sequences.
|
||||
/// \param line pointer to string
|
||||
/// \param cnt number of positions to move right
|
||||
/// \return moved pointer
|
||||
|
||||
idaman THREAD_SAFE const char *ida_export tag_advance(const char *line, int cnt);
|
||||
|
||||
|
||||
/// Move the pointer past all color codes.
|
||||
/// \param line can't be NULL
|
||||
/// \return moved pointer, can't be NULL
|
||||
|
||||
idaman THREAD_SAFE const char *ida_export tag_skipcodes(const char *line);
|
||||
|
||||
|
||||
/// Skip one color code.
|
||||
/// This function should be used if you are interested in color codes
|
||||
/// and want to analyze all of them.
|
||||
/// Otherwise tag_skipcodes() function is better since it will skip all colors at once.
|
||||
/// This function will skip the current color code if there is one.
|
||||
/// If the current symbol is not a color code, it will return the input.
|
||||
/// \return moved pointer
|
||||
|
||||
idaman THREAD_SAFE const char *ida_export tag_skipcode(const char *line);
|
||||
|
||||
|
||||
/// Calculate length of a colored string
|
||||
/// This function computes the length in unicode codepoints of a line
|
||||
/// \return the number of codepoints in the line, or -1 on error
|
||||
|
||||
idaman THREAD_SAFE ssize_t ida_export tag_strlen(const char *line);
|
||||
|
||||
|
||||
/// Remove color escape sequences from a string.
|
||||
/// \param buf output buffer with the string, cannot be NULL.
|
||||
/// \param str input string, cannot be NULL.
|
||||
/// \param init_level used to verify that COLOR_ON and COLOR_OFF tags are balanced
|
||||
/// \return length of resulting string, -1 if error
|
||||
|
||||
idaman THREAD_SAFE ssize_t ida_export tag_remove(qstring *buf, const char *str, int init_level=0);
|
||||
|
||||
inline THREAD_SAFE ssize_t idaapi tag_remove(qstring *buf, const qstring &str, int init_level=0)
|
||||
{
|
||||
return tag_remove(buf, str.c_str(), init_level);
|
||||
}
|
||||
|
||||
inline THREAD_SAFE ssize_t idaapi tag_remove(qstring *buf, int init_level=0)
|
||||
{
|
||||
if ( buf->empty() )
|
||||
return 0;
|
||||
return tag_remove(buf, buf->begin(), init_level);
|
||||
}
|
||||
|
||||
//@} color_conv
|
||||
|
||||
//@} color_def
|
||||
|
||||
|
||||
/// Get prefix color for line at 'ea'
|
||||
/// \return \ref COLOR_PFX
|
||||
idaman color_t ida_export calc_prefix_color(ea_t ea);
|
||||
|
||||
/// Get background color for line at 'ea'
|
||||
/// \return RGB color
|
||||
idaman bgcolor_t ida_export calc_bg_color(ea_t ea);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// S O U R C E F I L E S
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
/// \name Source files
|
||||
/// IDA can keep information about source files used to create the program.
|
||||
/// Each source file is represented by a range of addresses.
|
||||
/// A source file may contain several address ranges.
|
||||
//@{
|
||||
|
||||
/// Mark a range of address as belonging to a source file.
|
||||
/// An address range may belong only to one source file.
|
||||
/// A source file may be represented by several address ranges.
|
||||
/// \param ea1 linear address of start of the address range
|
||||
/// \param ea2 linear address of end of the address range (excluded)
|
||||
/// \param filename name of source file.
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export add_sourcefile(ea_t ea1, ea_t ea2, const char *filename);
|
||||
|
||||
|
||||
/// Get name of source file occupying the given address.
|
||||
/// \param ea linear address
|
||||
/// \param bounds pointer to the output buffer with the address range
|
||||
/// for the current file. May be NULL.
|
||||
/// \return NULL if source file information is not found,
|
||||
/// otherwise returns pointer to file name
|
||||
|
||||
idaman const char *ida_export get_sourcefile(ea_t ea, range_t *bounds=NULL);
|
||||
|
||||
|
||||
/// Delete information about the source file.
|
||||
/// \param ea linear address
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export del_sourcefile(ea_t ea);
|
||||
//@}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// G E N E R A T I O N O F D I S A S S E M B L E D T E X T
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
/// \name Generation of disassembled text
|
||||
//@{
|
||||
|
||||
/// User-defined line-prefixes are displayed just after the autogenerated
|
||||
/// line prefixes in the disassembly listing.
|
||||
/// There is no need to call this function explicitly.
|
||||
/// Use the user_defined_prefix_t class.
|
||||
/// \param prefix_len prefixed length. if 0, then uninstall UDP
|
||||
/// \param udp object to generate user-defined prefix
|
||||
/// \param install true:install, false:uninstall
|
||||
/// \param owner pointer to the plugin_t that owns UDP
|
||||
/// if non-NULL, then the object will be uninstalled and destroyed
|
||||
/// when the plugin gets unloaded
|
||||
idaman bool ida_export install_user_defined_prefix(
|
||||
size_t prefix_len,
|
||||
struct user_defined_prefix_t *udp,
|
||||
const void *owner);
|
||||
|
||||
/// Class to generate user-defined prefixes in the disassembly listing.
|
||||
struct user_defined_prefix_t
|
||||
{
|
||||
/// Creating a user-defined prefix object installs it.
|
||||
user_defined_prefix_t(size_t prefix_len, const void *owner)
|
||||
{
|
||||
install_user_defined_prefix(prefix_len, this, owner);
|
||||
}
|
||||
|
||||
/// Destroying a user-defined prefix object uninstalls it.
|
||||
virtual idaapi ~user_defined_prefix_t()
|
||||
{
|
||||
install_user_defined_prefix(0, this, nullptr);
|
||||
}
|
||||
|
||||
// Get a user-defined prefix.
|
||||
/// This callback must be overridden by the derived class.
|
||||
/// \param out the output buffer
|
||||
/// \param ea the current address
|
||||
/// \param insn the current instruction. if the current item is not
|
||||
/// an instruction, then insn.itype is zero.
|
||||
/// \param indent see explanations for \ref gen_printf()
|
||||
/// \param line the line to be generated.
|
||||
/// the line usually contains color tags.
|
||||
/// this argument can be examined to decide
|
||||
/// whether to generate the prefix.
|
||||
virtual void idaapi get_user_defined_prefix(
|
||||
qstring *vout,
|
||||
ea_t ea,
|
||||
const class insn_t &insn,
|
||||
int lnnum,
|
||||
int indent,
|
||||
const char *line) = 0;
|
||||
};
|
||||
|
||||
//@}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// A N T E R I O R / P O S T E R I O R L I N E S
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
/// \name Anterior/Posterior lines
|
||||
//@{
|
||||
|
||||
/// See higher level functions below
|
||||
|
||||
idaman AS_PRINTF(3, 0) bool ida_export vadd_extra_line(
|
||||
ea_t ea,
|
||||
int vel_flags, // see VEL_...
|
||||
const char *format,
|
||||
va_list va);
|
||||
|
||||
#define VEL_POST 0x01 // append posterior line
|
||||
#define VEL_CMT 0x02 // append comment line
|
||||
|
||||
|
||||
/// Add anterior/posterior non-comment line(s).
|
||||
/// \param ea linear address
|
||||
/// \param isprev do we add anterior lines? (0-no, posterior)
|
||||
/// \param format printf() style format string. may contain \\n to denote new lines.
|
||||
/// \return true if success
|
||||
|
||||
AS_PRINTF(3, 4) inline bool add_extra_line(ea_t ea, bool isprev, const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
int vel_flags = (isprev ? 0 : VEL_POST);
|
||||
bool ok = vadd_extra_line(ea, vel_flags, format, va);
|
||||
va_end(va);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/// Add anterior/posterior comment line(s).
|
||||
/// \param ea linear address
|
||||
/// \param isprev do we add anterior lines? (0-no, posterior)
|
||||
/// \param format printf() style format string. may contain \\n to denote
|
||||
/// new lines. The resulting string should not contain comment
|
||||
/// characters (;), the kernel will add them automatically.
|
||||
/// \return true if success
|
||||
|
||||
AS_PRINTF(3, 4) inline bool add_extra_cmt(ea_t ea, bool isprev, const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
int vel_flags = (isprev ? 0 : VEL_POST) | VEL_CMT;
|
||||
bool ok = vadd_extra_line(ea, vel_flags, format, va);
|
||||
va_end(va);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/// Add anterior comment line(s) at the start of program.
|
||||
/// \param format printf() style format string. may contain \\n to denote
|
||||
/// new lines. The resulting string should not contain comment
|
||||
/// characters (;), the kernel will add them automatically.
|
||||
/// \return true if success
|
||||
|
||||
AS_PRINTF(1, 2) inline bool add_pgm_cmt(const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
bool ok = vadd_extra_line(inf_get_min_ea(), VEL_CMT, format, va);
|
||||
va_end(va);
|
||||
return ok;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
///---------------------------------------------------------------------\cond
|
||||
/// The following functions are used in kernel only:
|
||||
|
||||
// Generate disassembly (many lines) and put them into a buffer
|
||||
// Returns number of generated lines
|
||||
idaman int ida_export generate_disassembly(
|
||||
qstrvec_t *out, // buffer to hold generated lines
|
||||
int *lnnum, // number of "the most interesting" line
|
||||
ea_t ea, // address to generate disassembly for
|
||||
int maxsize, // maximum number of lines
|
||||
bool as_stack); // Display undefined items as 2/4/8 bytes
|
||||
|
||||
// Generate one line of disassembly
|
||||
// This function discards all "non-interesting" lines
|
||||
// It is designed to generate one-line descriptions
|
||||
// of addresses for lists, etc.
|
||||
idaman bool ida_export generate_disasm_line(
|
||||
qstring *buf, // output buffer
|
||||
ea_t ea, // address to generate disassembly for
|
||||
int flags=0);
|
||||
#define GENDSM_FORCE_CODE (1 << 0) // generate a disassembly line as if
|
||||
// there is an instruction at 'ea'
|
||||
#define GENDSM_MULTI_LINE (1 << 1) // if the instruction consists of several lines,
|
||||
// produce all of them (useful for parallel instructions)
|
||||
#define GENDSM_REMOVE_TAGS (1 << 2) // remove tags from output buffer
|
||||
|
||||
/// Get length of the line prefix that was used for the last generated line
|
||||
|
||||
idaman int ida_export get_last_pfxlen(void);
|
||||
|
||||
|
||||
// Get pointer to the sequence of characters denoting 'close comment'
|
||||
// empty string means no comment (the current assembler has no open-comment close-comment pairs)
|
||||
// This function uses ash.cmnt2
|
||||
|
||||
idaman const char *ida_export closing_comment(void);
|
||||
|
||||
|
||||
// Every anterior/posterior line has its number.
|
||||
// Anterior lines have numbers from E_PREV
|
||||
// Posterior lines have numbers from E_NEXT
|
||||
|
||||
const int E_PREV = 1000;
|
||||
const int E_NEXT = 2000;
|
||||
|
||||
idaman int ida_export get_first_free_extra_cmtidx(ea_t ea, int start);
|
||||
idaman void ida_export update_extra_cmt(ea_t ea, int what, const char *str);
|
||||
idaman void ida_export del_extra_cmt(ea_t ea, int what);
|
||||
idaman ssize_t ida_export get_extra_cmt(qstring *buf, ea_t ea, int what);
|
||||
idaman void ida_export delete_extra_cmts(ea_t ea, int what);
|
||||
|
||||
idaman ea_t ida_export align_down_to_stack(ea_t newea);
|
||||
idaman ea_t ida_export align_up_to_stack(ea_t ea1, ea_t ea2=BADADDR);
|
||||
|
||||
// A helper class, to encode from UTF-8, -> into the target encoding.
|
||||
// This is typically used when generating listings (or any kind of
|
||||
// output file.)
|
||||
struct encoder_t
|
||||
{
|
||||
// whether or not a message should be printed, letting the
|
||||
// user know that some text couldn't be recoded properly
|
||||
enum notify_recerr_t
|
||||
{
|
||||
nr_none,
|
||||
nr_once,
|
||||
};
|
||||
|
||||
virtual ~encoder_t() {}
|
||||
virtual bool idaapi get_bom(bytevec_t *out) const = 0;
|
||||
// returns true if conversion was entirely successful, false otherwise.
|
||||
// codepoints that couldn't be converted, will be output as C
|
||||
// literal-escaped UTF-8 sequences (e.g., "\xC3\xD9"), and if
|
||||
// 'nr_once' was passed at creation-time, a one-time notification
|
||||
// well be output in the messages window.
|
||||
virtual bool idaapi encode(qstring *s) const = 0;
|
||||
// encode()s the UTF-8 string composed by format + args, and
|
||||
// returns true if all the resulting bytes could be written to
|
||||
// the output file.
|
||||
AS_PRINTF(3, 4) virtual bool idaapi print(FILE *out, const char *format, ...) const = 0;
|
||||
// should a file be opened as binary, or should it rather be opened
|
||||
// in text mode? This will have an importance in how '\n' characters
|
||||
// are possibly converted into '\x0A\x0D' on windows, which is most
|
||||
// inappropriate when output'ing e.g., UTF-16, UTF-32..
|
||||
virtual bool idaapi requires_binary_mode() const = 0;
|
||||
};
|
||||
|
||||
// Create the encoder with the given target encoding. If -1 is passed
|
||||
// then the effective target encoding will be computed like so:
|
||||
// if ( encidx < 0 )
|
||||
// {
|
||||
// encidx = get_outfile_encoding_idx();
|
||||
// if ( encidx == STRENC_DEFAULT )
|
||||
// encidx = get_default_encoding_idx(BPU_1B);
|
||||
// }
|
||||
idaman encoder_t *ida_export create_encoding_helper(
|
||||
int encidx=-1,
|
||||
encoder_t::notify_recerr_t nr=encoder_t::nr_once);
|
||||
|
||||
/// Callback functions to output lines:
|
||||
//@{
|
||||
typedef int idaapi html_header_cb_t(FILE *fp);
|
||||
typedef int idaapi html_footer_cb_t(FILE *fp);
|
||||
typedef int idaapi html_line_cb_t(
|
||||
FILE *fp,
|
||||
const qstring &line,
|
||||
bgcolor_t prefix_color,
|
||||
bgcolor_t bg_color);
|
||||
#define gen_outline_t html_line_cb_t
|
||||
//@}
|
||||
|
||||
///-------------------------------------------------------------------\endcond
|
||||
|
||||
|
||||
#ifndef NO_OBSOLETE_FUNCS
|
||||
idaman DEPRECATED void ida_export set_user_defined_prefix( // use install_user_defined_prefix()
|
||||
size_t width,
|
||||
void (idaapi *get_user_defined_prefix)(
|
||||
qstring *buf,
|
||||
ea_t ea,
|
||||
int lnnum,
|
||||
int indent,
|
||||
const char *line));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,293 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LLONG_HPP
|
||||
#define _LLONG_HPP
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
typedef unsigned __int64 ulonglong;
|
||||
typedef __int64 longlong;
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
typedef unsigned long long ulonglong;
|
||||
typedef long long longlong;
|
||||
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#ifdef __cplusplus
|
||||
inline constexpr longlong make_longlong(uint32 ll,int32 hh) { return ll | (longlong(hh) << 32); }
|
||||
inline constexpr ulonglong make_ulonglong(uint32 ll,int32 hh) { return ll | (ulonglong(hh) << 32); }
|
||||
inline uint32 low(const ulonglong &x) { return uint32(x); }
|
||||
inline uint32 high(const ulonglong &x) { return uint32(x>>32); }
|
||||
inline uint32 low(const longlong &x) { return uint32(x); }
|
||||
inline int32 high(const longlong &x) { return uint32(x>>32); }
|
||||
#else
|
||||
#define make_longlong(ll,hh) (ll | (longlong(hh) << 32))
|
||||
#define make_ulonglong(ll,hh) (ll | (ulonglong(hh) << 32))
|
||||
#endif
|
||||
|
||||
idaman THREAD_SAFE longlong ida_export llong_scan(
|
||||
const char *buf,
|
||||
int radix,
|
||||
const char **end);
|
||||
#ifndef swap64
|
||||
idaman THREAD_SAFE ulonglong ida_export swap64(ulonglong);
|
||||
# ifdef __cplusplus
|
||||
inline longlong swap64(longlong x)
|
||||
{
|
||||
return longlong(swap64(ulonglong(x)));
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// 128 BIT NUMBERS
|
||||
//---------------------------------------------------------------------------
|
||||
#ifdef __HAS_INT128__
|
||||
|
||||
typedef unsigned __int128 uint128;
|
||||
typedef __int128 int128;
|
||||
|
||||
inline int128 make_int128(ulonglong ll,longlong hh) { return ll | (int128(hh) << 64); }
|
||||
inline uint128 make_uint128(ulonglong ll,ulonglong hh) { return ll | (uint128(hh) << 64); }
|
||||
inline ulonglong low(const uint128 &x) { return ulonglong(x); }
|
||||
inline ulonglong high(const uint128 &x) { return ulonglong(x>>64); }
|
||||
inline ulonglong low(const int128 &x) { return ulonglong(x); }
|
||||
inline longlong high(const int128 &x) { return ulonglong(x>>64); }
|
||||
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
//-V:uint128:730 not all members of a class are initialized inside the constructor
|
||||
class uint128
|
||||
{
|
||||
ulonglong l;
|
||||
ulonglong h;
|
||||
friend class int128;
|
||||
public:
|
||||
uint128(void) {}
|
||||
uint128(uint x) { l = x; h = 0; }
|
||||
uint128(int x) { l = x; h = (x < 0)? -1 : 0; }
|
||||
uint128(ulonglong x) { l = x; h = 0; }
|
||||
uint128(longlong x) { l = x; h = (x < 0) ? -1 : 0; }
|
||||
uint128(ulonglong ll, ulonglong hh) { l = ll; h = hh; }
|
||||
friend ulonglong low (const uint128 &x) { return x.l; }
|
||||
friend ulonglong high(const uint128 &x) { return x.h; }
|
||||
friend uint128 operator+(const uint128 &x, const uint128 &y);
|
||||
friend uint128 operator-(const uint128 &x, const uint128 &y);
|
||||
friend uint128 operator/(const uint128 &x, const uint128 &y);
|
||||
friend uint128 operator%(const uint128 &x, const uint128 &y);
|
||||
friend uint128 operator*(const uint128 &x, const uint128 &y);
|
||||
friend uint128 operator|(const uint128 &x, const uint128 &y);
|
||||
friend uint128 operator&(const uint128 &x, const uint128 &y);
|
||||
friend uint128 operator^(const uint128 &x, const uint128 &y);
|
||||
friend uint128 operator>>(const uint128 &x, int cnt);
|
||||
friend uint128 operator<<(const uint128 &x, int cnt);
|
||||
uint128 &operator+=(const uint128 &y);
|
||||
uint128 &operator-=(const uint128 &y);
|
||||
uint128 &operator/=(const uint128 &y);
|
||||
uint128 &operator%=(const uint128 &y);
|
||||
uint128 &operator*=(const uint128 &y);
|
||||
uint128 &operator|=(const uint128 &y);
|
||||
uint128 &operator&=(const uint128 &y);
|
||||
uint128 &operator^=(const uint128 &y);
|
||||
uint128 &operator>>=(int cnt);
|
||||
uint128 &operator<<=(int cnt);
|
||||
uint128 &operator++(void);
|
||||
uint128 &operator--(void);
|
||||
friend uint128 operator+(const uint128 &x) { return x; }
|
||||
friend uint128 operator-(const uint128 &x);
|
||||
friend uint128 operator~(const uint128 &x) { return uint128(~x.l,~x.h); }
|
||||
friend int operator==(const uint128 &x, const uint128 &y) { return x.l == y.l && x.h == y.h; }
|
||||
friend int operator!=(const uint128 &x, const uint128 &y) { return x.l != y.l || x.h != y.h; }
|
||||
friend int operator> (const uint128 &x, const uint128 &y) { return x.h > y.h || (x.h == y.h && x.l > y.l); }
|
||||
friend int operator< (const uint128 &x, const uint128 &y) { return x.h < y.h || (x.h == y.h && x.l < y.l); }
|
||||
friend int operator>=(const uint128 &x, const uint128 &y) { return x.h > y.h || (x.h == y.h && x.l >= y.l); }
|
||||
friend int operator<=(const uint128 &x, const uint128 &y) { return x.h < y.h || (x.h == y.h && x.l <= y.l); }
|
||||
};
|
||||
|
||||
//-V:int128:730 not all members of a class are initialized inside the constructor
|
||||
class int128
|
||||
{
|
||||
ulonglong l;
|
||||
longlong h;
|
||||
friend class uint128;
|
||||
public:
|
||||
int128(void) {}
|
||||
int128(uint x) { l = x; h = 0; }
|
||||
int128(int x) { l = x; h = (x < 0) ? -1 : 0; }
|
||||
int128(ulonglong x) { l = x; h = 0; }
|
||||
int128(longlong x) { l = x; h = (x < 0) ? -1 : 0; }
|
||||
int128(ulonglong ll, ulonglong hh) { l=ll; h=hh; }
|
||||
int128(const uint128 &x) { l=x.l; h=x.h; }
|
||||
friend ulonglong low (const int128 &x) { return x.l; }
|
||||
friend ulonglong high(const int128 &x) { return x.h; }
|
||||
friend int128 operator+(const int128 &x, const int128 &y);
|
||||
friend int128 operator-(const int128 &x, const int128 &y);
|
||||
friend int128 operator/(const int128 &x, const int128 &y);
|
||||
friend int128 operator%(const int128 &x, const int128 &y);
|
||||
friend int128 operator*(const int128 &x, const int128 &y);
|
||||
friend int128 operator|(const int128 &x, const int128 &y);
|
||||
friend int128 operator&(const int128 &x, const int128 &y);
|
||||
friend int128 operator^(const int128 &x, const int128 &y);
|
||||
friend int128 operator>>(const int128 &x, int cnt);
|
||||
friend int128 operator<<(const int128 &x, int cnt);
|
||||
int128 &operator+=(const int128 &y);
|
||||
int128 &operator-=(const int128 &y);
|
||||
int128 &operator/=(const int128 &y);
|
||||
int128 &operator%=(const int128 &y);
|
||||
int128 &operator*=(const int128 &y);
|
||||
int128 &operator|=(const int128 &y);
|
||||
int128 &operator&=(const int128 &y);
|
||||
int128 &operator^=(const int128 &y);
|
||||
int128 &operator>>=(int cnt);
|
||||
int128 &operator<<=(int cnt);
|
||||
int128 &operator++(void);
|
||||
int128 &operator--(void);
|
||||
friend int128 operator+(const int128 &x) { return x; }
|
||||
friend int128 operator-(const int128 &x);
|
||||
friend int128 operator~(const int128 &x) { return int128(~x.l,~x.h); }
|
||||
friend int operator==(const int128 &x, const int128 &y) { return x.l == y.l && x.h == y.h; }
|
||||
friend int operator!=(const int128 &x, const int128 &y) { return x.l != y.l || x.h != y.h; }
|
||||
friend int operator> (const int128 &x, const int128 &y) { return x.h > y.h || (x.h == y.h && x.l > y.l); }
|
||||
friend int operator< (const int128 &x, const int128 &y) { return x.h < y.h || (x.h == y.h && x.l < y.l); }
|
||||
friend int operator>=(const int128 &x, const int128 &y) { return x.h > y.h || (x.h == y.h && x.l >= y.l); }
|
||||
friend int operator<=(const int128 &x, const int128 &y) { return x.h < y.h || (x.h == y.h && x.l <= y.l); }
|
||||
};
|
||||
|
||||
inline int128 make_int128(ulonglong ll, longlong hh) { return int128(ll, hh); }
|
||||
inline uint128 make_uint128(ulonglong ll, longlong hh) { return uint128(ll, hh); }
|
||||
idaman THREAD_SAFE void ida_export swap128(uint128 *x);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 operator+(const uint128 &x, const uint128 &y)
|
||||
{
|
||||
ulonglong h = x.h + y.h;
|
||||
ulonglong l = x.l + y.l;
|
||||
if ( l < x.l )
|
||||
h = h + 1;
|
||||
return uint128(l,h);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 operator-(const uint128 &x, const uint128 &y)
|
||||
{
|
||||
ulonglong h = x.h - y.h;
|
||||
ulonglong l = x.l - y.l;
|
||||
if ( l > x.l )
|
||||
h = h - 1;
|
||||
return uint128(l,h);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 operator|(const uint128 &x, const uint128 &y)
|
||||
{
|
||||
return uint128(x.l | y.l, x.h | y.h);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 operator&(const uint128 &x, const uint128 &y)
|
||||
{
|
||||
return uint128(x.l & y.l, x.h & y.h);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 operator^(const uint128 &x, const uint128 &y)
|
||||
{
|
||||
return uint128(x.l ^ y.l, x.h ^ y.h);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator+=(const uint128 &y)
|
||||
{
|
||||
return *this = *this + y;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator-=(const uint128 &y)
|
||||
{
|
||||
return *this = *this - y;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator|=(const uint128 &y)
|
||||
{
|
||||
return *this = *this | y;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator&=(const uint128 &y)
|
||||
{
|
||||
return *this = *this & y;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator^=(const uint128 &y)
|
||||
{
|
||||
return *this = *this ^ y;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator/=(const uint128 &y)
|
||||
{
|
||||
return *this = *this / y;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator%=(const uint128 &y)
|
||||
{
|
||||
return *this = *this % y;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator*=(const uint128 &y)
|
||||
{
|
||||
return *this = *this * y;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator<<=(int cnt)
|
||||
{
|
||||
return *this = *this << cnt;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator>>=(int cnt)
|
||||
{
|
||||
return *this = *this >> cnt;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator++(void)
|
||||
{
|
||||
if ( ++l == 0 )
|
||||
++h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 &uint128::operator--(void)
|
||||
{
|
||||
if ( l == 0 )
|
||||
--h;
|
||||
--l;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint128 operator-(const uint128 &x)
|
||||
{
|
||||
return ~x + 1;
|
||||
}
|
||||
|
||||
#endif // ifdef __cplusplus
|
||||
#endif // ifdef __HAS_INT128__
|
||||
|
||||
#endif // define _LLONG_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,384 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MOVES_HPP
|
||||
#define __MOVES_HPP
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct graph_location_info_t
|
||||
{
|
||||
double zoom; // zoom level, 1.0 == 100%, 0 means auto position
|
||||
double orgx; // graph origin, x coord
|
||||
double orgy; // graph origin, y coord
|
||||
graph_location_info_t(void) : zoom(0), orgx(0), orgy(0) {}
|
||||
bool operator == (const graph_location_info_t &r) const
|
||||
{ return zoom == r.zoom && orgx == r.orgx && orgy == r.orgy; } //-V550 An odd precise comparison: zoom == r.zoom
|
||||
bool operator != (const graph_location_info_t &r) const
|
||||
{ return !(*this == r); }
|
||||
void serialize(bytevec_t *out) const;
|
||||
bool deserialize(memory_deserializer_t &mmdsr);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
inline void graph_location_info_t::serialize(bytevec_t *out) const
|
||||
{
|
||||
CASSERT(sizeof(graph_location_info_t) == 3*8);
|
||||
out->append(this, sizeof(graph_location_info_t));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
inline bool graph_location_info_t::deserialize(memory_deserializer_t &mmdsr)
|
||||
{
|
||||
return mmdsr.unpack_obj(this, sizeof(graph_location_info_t)) != NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct segm_move_info_t
|
||||
{
|
||||
segm_move_info_t(ea_t _from = 0, ea_t _to = 0, size_t _sz = 0)
|
||||
: from(_from), to(_to), size(_sz) {}
|
||||
ea_t from, to;
|
||||
size_t size;
|
||||
|
||||
bool operator == (const segm_move_info_t &r) const
|
||||
{ return from == r.from && to == r.to && size == r.size; }
|
||||
bool operator != (const segm_move_info_t &r) const
|
||||
{ return !(*this == r); }
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(segm_move_info_t);
|
||||
typedef qvector<segm_move_info_t> segm_move_info_vec_t;
|
||||
|
||||
struct segm_move_infos_t : public segm_move_info_vec_t
|
||||
{
|
||||
const segm_move_info_t *find(ea_t ea) const
|
||||
{
|
||||
for ( size_t i = 0; i < size(); ++i )
|
||||
{
|
||||
const segm_move_info_t &cur = at(i);
|
||||
if ( ea >= cur.from && ea < cur.from + cur.size )
|
||||
return &cur;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class place_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct renderer_info_pos_t // out of renderer_info_t, to enable SWiG parsing
|
||||
{
|
||||
int node;
|
||||
short cx;
|
||||
short cy;
|
||||
|
||||
renderer_info_pos_t() : node(-1), cx(-1), cy(-1) {}
|
||||
bool operator == (const renderer_info_pos_t &r) const
|
||||
{ return node == r.node && cx == r.cx && cy == r.cy; }
|
||||
bool operator != (const renderer_info_pos_t &r) const
|
||||
{ return !(*this == r); }
|
||||
void serialize(bytevec_t *out) const;
|
||||
bool deserialize(memory_deserializer_t &mmdsr);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
inline void renderer_info_pos_t::serialize(bytevec_t *out) const
|
||||
{
|
||||
out->pack_dd(node);
|
||||
out->pack_dw(cx);
|
||||
out->pack_dw(cy);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
inline bool renderer_info_pos_t::deserialize(memory_deserializer_t &mmdsr)
|
||||
{
|
||||
node = mmdsr.unpack_dd();
|
||||
cx = mmdsr.unpack_dw();
|
||||
if ( mmdsr.empty() )
|
||||
return false;
|
||||
cy = mmdsr.unpack_dw();
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct renderer_info_t
|
||||
{
|
||||
renderer_info_t() { clear(); }
|
||||
graph_location_info_t gli;
|
||||
typedef renderer_info_pos_t pos_t;
|
||||
pos_t pos;
|
||||
tcc_renderer_type_t rtype;
|
||||
|
||||
bool operator == (const renderer_info_t &r) const
|
||||
{ return rtype == r.rtype && pos == r.pos && gli == r.gli; }
|
||||
bool operator != (const renderer_info_t &r) const
|
||||
{ return !(*this == r); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
gli = {};
|
||||
pos = {};
|
||||
rtype = TCCRT_INVALID;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class lochist_t;
|
||||
struct lochist_entry_t;
|
||||
struct expanded_area_t;
|
||||
|
||||
#define LSEF_PLACE (1 << 0)
|
||||
#define LSEF_RINFO (1 << 1)
|
||||
#define LSEF_PTYPE (1 << 2)
|
||||
#define LSEF_ALL (LSEF_PLACE|LSEF_RINFO|LSEF_PTYPE)
|
||||
|
||||
#ifndef SWIG
|
||||
#define DEFINE_LOCHIST_T_HELPERS(decl) \
|
||||
decl void ida_export lochist_t_register_live(lochist_t &); \
|
||||
decl void ida_export lochist_t_deregister_live(lochist_t &); \
|
||||
decl bool ida_export lochist_t_init (lochist_t &, const char *, const place_t &, void *, uint32); \
|
||||
decl void ida_export lochist_t_jump (lochist_t &, bool try_to_unhide, const lochist_entry_t &e); \
|
||||
decl bool ida_export lochist_t_fwd (lochist_t &, uint32 cnt, bool try_to_unhide); \
|
||||
decl bool ida_export lochist_t_back (lochist_t &, uint32 cnt, bool try_to_unhide); \
|
||||
decl bool ida_export lochist_t_seek (lochist_t &, uint32 index, bool try_to_unhide, bool apply_cur); \
|
||||
decl const lochist_entry_t *ida_export lochist_t_get_current(const lochist_t &); \
|
||||
decl uint32 ida_export lochist_t_current_index(const lochist_t &); \
|
||||
decl void ida_export lochist_t_set (lochist_t &, uint32, const lochist_entry_t &); \
|
||||
decl bool ida_export lochist_t_get (lochist_entry_t *, const lochist_t &, uint32); \
|
||||
decl uint32 ida_export lochist_t_size (const lochist_t &);\
|
||||
decl void ida_export lochist_t_save (const lochist_t &); \
|
||||
decl void ida_export lochist_t_clear (lochist_t &);
|
||||
#else
|
||||
#define DEFINE_LOCHIST_T_HELPERS(decl)
|
||||
#endif // SWIG
|
||||
DEFINE_LOCHIST_T_HELPERS(idaman)
|
||||
|
||||
#ifndef SWIG
|
||||
#define DEFINE_LOCHIST_ENTRY_T_HELPERS(decl) \
|
||||
decl void ida_export lochist_entry_t_serialize(bytevec_t *, const lochist_entry_t &); \
|
||||
decl bool ida_export lochist_entry_t_deserialize(lochist_entry_t *, const uchar **, const uchar *const, const place_t *);
|
||||
#else
|
||||
#define DEFINE_LOCHIST_ENTRY_T_HELPERS(decl)
|
||||
#endif // SWIG
|
||||
DEFINE_LOCHIST_ENTRY_T_HELPERS(idaman)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct lochist_entry_t
|
||||
{
|
||||
renderer_info_t rinfo;
|
||||
place_t *plce;
|
||||
|
||||
lochist_entry_t() : plce(NULL) {}
|
||||
lochist_entry_t(const place_t *p, const renderer_info_t &r)
|
||||
: rinfo(r), plce((place_t *) p)
|
||||
{
|
||||
if ( plce != NULL )
|
||||
plce = plce->clone();
|
||||
}
|
||||
#ifndef SWIG
|
||||
lochist_entry_t(const lochist_t &s);
|
||||
#endif // SWIG
|
||||
lochist_entry_t(const lochist_entry_t &other) : plce(NULL) { *this = other; }
|
||||
~lochist_entry_t() { clear(); }
|
||||
const renderer_info_t &renderer_info() const { return rinfo; }
|
||||
const place_t *place() const { return plce; }
|
||||
|
||||
renderer_info_t &renderer_info() { return rinfo; }
|
||||
place_t *place() { return plce; }
|
||||
void set_place(const place_t *p) { clear(); plce = p == NULL ? NULL : p->clone(); }
|
||||
void set_place(const place_t &p) { set_place(&p); }
|
||||
|
||||
bool is_valid() const { return plce != NULL; }
|
||||
|
||||
lochist_entry_t &operator=(const lochist_entry_t &r)
|
||||
{
|
||||
clear();
|
||||
(*this).rinfo = r.rinfo;
|
||||
if ( r.plce != NULL )
|
||||
plce = r.plce->clone();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void acquire_place(place_t *in_p)
|
||||
{ clear(); plce = in_p; }
|
||||
|
||||
void serialize(bytevec_t *out) const { lochist_entry_t_serialize(out, *this); }
|
||||
bool deserialize(const uchar **ptr, const uchar *const end, const place_t *tmplate)
|
||||
{ return lochist_entry_t_deserialize(this, ptr, end, tmplate); }
|
||||
|
||||
private:
|
||||
void clear()
|
||||
{
|
||||
if ( plce != NULL )
|
||||
qfree(plce);
|
||||
}
|
||||
|
||||
friend class lochist_t;
|
||||
DEFINE_LOCHIST_T_HELPERS(friend)
|
||||
DEFINE_LOCHIST_ENTRY_T_HELPERS(friend)
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(lochist_entry_t);
|
||||
|
||||
#define UNHID_SEGM 0x0001 // unhid a segment at 'target'
|
||||
#define UNHID_FUNC 0x0002 // unhid a function at 'target'
|
||||
#define UNHID_RANGE 0x0004 // unhid an range at 'target'
|
||||
|
||||
#define DEFAULT_CURSOR_Y 0xFFFF
|
||||
#define DEFAULT_LNNUM -1
|
||||
#define CURLOC_LIST "$ curlocs"
|
||||
#define MAX_MARK_SLOT 1024 // Max number of marked locations
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class lochist_t
|
||||
{
|
||||
void *ud;
|
||||
|
||||
DEFINE_LOCHIST_T_HELPERS(friend)
|
||||
|
||||
lochist_entry_t cur;
|
||||
netnode node;
|
||||
|
||||
#define LHF_HISTORY_DISABLED (1 << 0) // enable history?
|
||||
uint32 flags;
|
||||
|
||||
public:
|
||||
lochist_t() : flags(0) { lochist_t_register_live(*this); }
|
||||
~lochist_t() { lochist_t_deregister_live(*this); }
|
||||
bool is_history_enabled() const { return (flags & LHF_HISTORY_DISABLED) == 0; }
|
||||
int get_place_id() const
|
||||
{
|
||||
const place_t *p = cur.place();
|
||||
return p == NULL ? -1 : p->id();
|
||||
}
|
||||
bool init(const char *stream_name, const place_t *_defpos, void *_ud, uint32 _flags)
|
||||
{ return lochist_t_init(*this, stream_name, *_defpos, _ud, _flags); }
|
||||
|
||||
nodeidx_t netcode() const
|
||||
{ return node; }
|
||||
|
||||
void jump(bool try_to_unhide, const lochist_entry_t &e)
|
||||
{ lochist_t_jump(*this, try_to_unhide, e); }
|
||||
|
||||
uint32 current_index() const
|
||||
{ return lochist_t_current_index(*this); }
|
||||
|
||||
bool seek(uint32 index, bool try_to_unhide)
|
||||
{ return lochist_t_seek(*this, index, try_to_unhide, true); }
|
||||
|
||||
bool fwd(uint32 cnt, bool try_to_unhide)
|
||||
{ return lochist_t_fwd(*this, cnt, try_to_unhide); }
|
||||
|
||||
bool back(uint32 cnt, bool try_to_unhide)
|
||||
{ return lochist_t_back(*this, cnt, try_to_unhide); }
|
||||
|
||||
void save() const
|
||||
{ lochist_t_save(*this); }
|
||||
|
||||
void clear()
|
||||
{ lochist_t_clear(*this); }
|
||||
|
||||
const lochist_entry_t &get_current() const
|
||||
{ return *lochist_t_get_current(*this); }
|
||||
|
||||
void set_current(const lochist_entry_t &e)
|
||||
{ return set(current_index(), e); }
|
||||
|
||||
void set(uint32 index, const lochist_entry_t &e)
|
||||
{ lochist_t_set(*this, index, e); }
|
||||
|
||||
bool get(lochist_entry_t *out, uint32 index) const
|
||||
{ return lochist_t_get(out, *this, index); }
|
||||
|
||||
uint32 size(void) const
|
||||
{ return lochist_t_size(*this); }
|
||||
|
||||
const place_t *get_template_place() const
|
||||
{ return cur.place(); }
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(lochist_t);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#ifndef SWIG
|
||||
idaman uint32 ida_export bookmarks_t_mark(const lochist_entry_t &, uint32, const char *, const char *, void *);
|
||||
idaman bool ida_export bookmarks_t_get(lochist_entry_t *, qstring *, uint32 *, void *);
|
||||
idaman bool ida_export bookmarks_t_get_desc(qstring *, const lochist_entry_t &, uint32, void *);
|
||||
idaman uint32 ida_export bookmarks_t_find_index(const lochist_entry_t &, void *);
|
||||
idaman uint32 ida_export bookmarks_t_size(const lochist_entry_t &, void *);
|
||||
idaman bool ida_export bookmarks_t_erase(const lochist_entry_t &, uint32, void *);
|
||||
#endif // SWIG
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class bookmarks_t
|
||||
{
|
||||
bookmarks_t(); // No.
|
||||
~bookmarks_t() {}
|
||||
public:
|
||||
#define BOOKMARKS_CHOOSE_INDEX (uint32(-1))
|
||||
#define BOOKMARKS_BAD_INDEX (uint32(-1))
|
||||
|
||||
// Mark/unmark position
|
||||
// index - the marked position number (0..MAX_MARK_SLOT)
|
||||
// if specified as BOOKMARKS_CHOOSE_INDEX: ask the user to select the mark slot.
|
||||
// title - if index == BOOKMARKS_CHOOSE_INDEX, then the window caption of
|
||||
// the dialog which will appear on the screen. title==NULL will
|
||||
// lead to the default caption: "please select a mark slot"
|
||||
// desc - description of the marked position. If NULL, IDA will show a
|
||||
// dialog box asking the user to enter the description.
|
||||
// returns used marker number (BOOKMARKS_BAD_INDEX - none)
|
||||
static uint32 mark(
|
||||
const lochist_entry_t &e,
|
||||
uint32 index,
|
||||
const char *title,
|
||||
const char *desc,
|
||||
void *ud)
|
||||
{ return bookmarks_t_mark(e, index, title, desc, ud); }
|
||||
|
||||
// 'out_entry' MUST:
|
||||
// - contain a valid place_t*; data will be deserialized into it
|
||||
// - have a valid, corresponding tcc_place_type_t
|
||||
static bool get(
|
||||
lochist_entry_t *out_entry,
|
||||
qstring *out_desc,
|
||||
uint32 *index, // index==BOOKMARKS_CHOOSE_INDEX? let the user choose
|
||||
void *ud)
|
||||
{ return bookmarks_t_get(out_entry, out_desc, index, ud); }
|
||||
|
||||
static bool get_desc(
|
||||
qstring *out,
|
||||
const lochist_entry_t &e,
|
||||
uint32 index,
|
||||
void *ud)
|
||||
{ return bookmarks_t_get_desc(out, e, index, ud); }
|
||||
|
||||
static uint32 find_index(
|
||||
const lochist_entry_t &e,
|
||||
void *ud)
|
||||
{ return bookmarks_t_find_index(e, ud); }
|
||||
|
||||
static uint32 size(
|
||||
const lochist_entry_t &e,
|
||||
void *ud)
|
||||
{ return bookmarks_t_size(e, ud); }
|
||||
|
||||
static bool erase(
|
||||
const lochist_entry_t &e,
|
||||
uint32 index,
|
||||
void *ud)
|
||||
{ return bookmarks_t_erase(e, index, ud); }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
inline lochist_entry_t::lochist_entry_t(const lochist_t &lh)
|
||||
: plce(NULL)
|
||||
{
|
||||
*this = lh.get_current();
|
||||
}
|
||||
|
||||
|
||||
#endif // __MOVES_HPP
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,786 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _NAME_HPP
|
||||
#define _NAME_HPP
|
||||
|
||||
#include <ida.hpp>
|
||||
|
||||
/*! \file name.hpp
|
||||
|
||||
\brief Functions that deal with names.
|
||||
|
||||
A non-tail address of the program may have a name.
|
||||
Tail addresses (i.e. the addresses in the middle of an instruction or
|
||||
data item) cannot have names.
|
||||
*/
|
||||
|
||||
class func_t; // funcs.hpp
|
||||
typedef uchar color_t; // lines.hpp
|
||||
|
||||
/// Maximum length of a name in IDA (with the trailing zero)
|
||||
#define MAXNAMELEN 512
|
||||
|
||||
|
||||
/// Name prefix used by IDA for the imported functions
|
||||
#define FUNC_IMPORT_PREFIX "__imp_"
|
||||
|
||||
|
||||
/// Set or delete name of an item at the specified address.
|
||||
/// An item can be anything: instruction, function, data byte, word, string,
|
||||
/// structure, etc...
|
||||
/// Include name into the list of names.
|
||||
/// \param ea linear address.
|
||||
/// do nothing if ea is not valid (return 0).
|
||||
/// tail bytes can't have names.
|
||||
/// \param name new name.
|
||||
/// - NULL: do nothing (return 0).
|
||||
/// - "" : delete name.
|
||||
/// - otherwise this is a new name.
|
||||
/// \param flags \ref SN_.
|
||||
/// If a bit is not specified, then the corresponding action is not performed
|
||||
/// and the name will retain the same bits as before calling this function.
|
||||
/// For new names, default is: non-public, non-weak, non-auto.
|
||||
/// \retval 1 ok, name is changed
|
||||
/// \retval 0 failure, a warning is displayed
|
||||
|
||||
idaman bool ida_export set_name(ea_t ea, const char *name, int flags=0);
|
||||
|
||||
/// \defgroup SN_ Set name flags
|
||||
/// Passed as 'flag' parameter to set_name(ea_t, const char *, int)
|
||||
//@{
|
||||
#define SN_CHECK 0x00
|
||||
#define SN_NOCHECK 0x01 ///< Don't fail if the name contains invalid characters.
|
||||
///< If this bit is clear, all invalid chars
|
||||
///< (those !is_ident_cp()) will be replaced
|
||||
///< by SUBSTCHAR
|
||||
///< List of valid characters is defined in ida.cfg
|
||||
#define SN_PUBLIC 0x02 ///< if set, make name public
|
||||
#define SN_NON_PUBLIC 0x04 ///< if set, make name non-public
|
||||
#define SN_WEAK 0x08 ///< if set, make name weak
|
||||
#define SN_NON_WEAK 0x10 ///< if set, make name non-weak
|
||||
#define SN_AUTO 0x20 ///< if set, make name autogenerated
|
||||
#define SN_NON_AUTO 0x40 ///< if set, make name non-autogenerated
|
||||
#define SN_NOLIST 0x80 ///< if set, exclude name from the list.
|
||||
///< if not set, then include the name into
|
||||
///< the list (however, if other bits are set,
|
||||
///< the name might be immediately excluded
|
||||
///< from the list).
|
||||
#define SN_NOWARN 0x100 ///< don't display a warning if failed
|
||||
#define SN_LOCAL 0x200 ///< create local name. a function should exist.
|
||||
///< local names can't be public or weak.
|
||||
///< also they are not included into the list of names
|
||||
///< they can't have dummy prefixes.
|
||||
#define SN_IDBENC 0x400 ///< the name is given in the IDB encoding;
|
||||
///< non-ASCII bytes will be decoded accordingly.
|
||||
///< Specifying SN_IDBENC also implies SN_NODUMMY
|
||||
#define SN_FORCE 0x800 ///< if the specified name is already present
|
||||
///< in the database, try variations with a
|
||||
///< numerical suffix like "_123"
|
||||
#define SN_NODUMMY 0x1000 ///< automatically prepend the name with '_' if it
|
||||
///< begins with a dummy suffix such as 'sub_'.
|
||||
///< See also SN_IDBENC
|
||||
#define SN_DELTAIL 0x2000 ///< if name cannot be set because of a tail byte,
|
||||
///< delete the hindering item
|
||||
//@}
|
||||
|
||||
inline bool force_name(ea_t ea, const char *name, int flags=0)
|
||||
{
|
||||
return set_name(ea, name, flags|SN_FORCE|SN_NODUMMY);
|
||||
}
|
||||
|
||||
/// \name Delete a name of a program item
|
||||
/// \param ea linear address
|
||||
/// \retval 1 ok, name is deleted
|
||||
/// \retval 0 failure, invalid address
|
||||
//@{
|
||||
inline bool del_global_name(ea_t ea) { return set_name(ea,"", SN_NOWARN); }
|
||||
inline bool del_local_name(ea_t ea) { return set_name(ea,"", SN_LOCAL|SN_NOWARN); }
|
||||
//@}
|
||||
|
||||
/// Give an autogenerated (dummy) name.
|
||||
/// Autogenerated names have special prefixes (loc_...).
|
||||
/// \param from linear address of the operand which references to the address
|
||||
/// \param ea linear address
|
||||
/// \retval 1 ok, dummy name is generated or the byte already had a name
|
||||
/// \retval 0 failure, invalid address or tail byte
|
||||
|
||||
idaman bool ida_export set_dummy_name(ea_t from, ea_t ea); // give dummy name
|
||||
|
||||
|
||||
/// \name Set/Clear bit in flags for 'autogenerated but meaningful name'
|
||||
/// This bit affects value of has_user_name(), has_auto_name() functions.
|
||||
/// \param ea linear address
|
||||
/// \retval 1 ok
|
||||
/// \retval 0 no meaningful name is present at the specified address
|
||||
//@{
|
||||
idaman bool ida_export make_name_auto(ea_t ea);
|
||||
idaman bool ida_export make_name_user(ea_t ea);
|
||||
//@}
|
||||
|
||||
|
||||
enum ucdr_kind_t
|
||||
{
|
||||
UCDR_STRLIT = 0x01, // string literals
|
||||
UCDR_NAME = 0x02, // regular (unmangled) names
|
||||
UCDR_MANGLED = 0x04, // mangled names
|
||||
UCDR_TYPE = 0x08, // type names
|
||||
};
|
||||
|
||||
enum nametype_t
|
||||
{
|
||||
// identifier (e.g., function name)
|
||||
VNT_IDENT = UCDR_NAME|UCDR_MANGLED,
|
||||
// type name (can contain '<', '>', ...)
|
||||
VNT_TYPE = UCDR_TYPE,
|
||||
// UDT (structure, union, enum) member
|
||||
VNT_UDTMEM = UCDR_NAME,
|
||||
// string literal
|
||||
VNT_STRLIT = UCDR_STRLIT,
|
||||
VNT_VISIBLE = VNT_UDTMEM,// visible cp (obsolete; will be deleted)
|
||||
};
|
||||
|
||||
/// Validate a name.
|
||||
/// This function replaces all invalid characters in the name with SUBSTCHAR.
|
||||
/// However, it will return false if name is valid but not allowed to be an
|
||||
/// identifier (is a register name).
|
||||
///
|
||||
/// \param[in,out] name ptr to name. the name will be modified
|
||||
/// \param type the type of name we want to validate
|
||||
/// \param flags see SN_* . Only SN_IDBENC is currently considered
|
||||
///
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export validate_name(
|
||||
qstring *name,
|
||||
nametype_t type,
|
||||
int flags = 0);
|
||||
|
||||
|
||||
/// Is the given codepoint acceptable in the given context?
|
||||
|
||||
idaman bool ida_export is_valid_cp(wchar32_t cp, nametype_t kind, void *data=NULL);
|
||||
|
||||
|
||||
/// Mark the given codepoint (or range) as acceptable or unacceptable in the given context
|
||||
/// If 'endcp' is not BADCP, it is considered to be the end of the range:
|
||||
/// [cp, endcp), and is not included in the range
|
||||
|
||||
idaman void ida_export set_cp_validity(ucdr_kind_t kind, wchar32_t cp, wchar32_t endcp=BADCP, bool valid=true);
|
||||
|
||||
|
||||
/// Is the given codepoint (or range) acceptable in the given context?
|
||||
/// If 'endcp' is not BADCP, it is considered to be the end of the range:
|
||||
/// [cp, endcp), and is not included in the range
|
||||
|
||||
idaman bool ida_export get_cp_validity(ucdr_kind_t kind, wchar32_t cp, wchar32_t endcp=BADCP);
|
||||
|
||||
|
||||
/// Can a character appear in a name? (present in ::NameChars or ::MangleChars)
|
||||
|
||||
inline bool is_ident_cp(wchar32_t cp) { return is_valid_cp(cp, VNT_IDENT); }
|
||||
|
||||
|
||||
/// Can a character appear in a string literal (present in ::StrlitChars)
|
||||
/// If 'specific_ranges' are specified, those will be used instead of
|
||||
/// the ones corresponding to the current culture (only if ::StrlitChars
|
||||
/// is configured to use the current culture)
|
||||
|
||||
inline bool is_strlit_cp(wchar32_t cp, const rangeset_crefvec_t *specific_ranges=NULL)
|
||||
{ return is_valid_cp(cp, VNT_STRLIT, (void *) specific_ranges); }
|
||||
|
||||
|
||||
/// Can a character be displayed in a name? (present in ::NameChars)
|
||||
|
||||
inline bool is_visible_cp(wchar32_t cp)
|
||||
{ return is_valid_cp(cp, VNT_VISIBLE); }
|
||||
|
||||
|
||||
/// Is a valid name? (including ::MangleChars)
|
||||
|
||||
idaman bool ida_export is_ident(const char *name);
|
||||
|
||||
|
||||
/// Is valid user-specified name? (valid name & !dummy prefix).
|
||||
/// \param name name to test. may be NULL.
|
||||
/// \retval 1 yes
|
||||
/// \retval 0 no
|
||||
|
||||
idaman bool ida_export is_uname(const char *name);
|
||||
|
||||
|
||||
/// Is valid type name?
|
||||
/// \param name name to test. may be NULL.
|
||||
/// \retval 1 yes
|
||||
/// \retval 0 no
|
||||
|
||||
idaman bool ida_export is_valid_typename(const char *name);
|
||||
|
||||
|
||||
/// Is dummy name?
|
||||
/// \param name name to test. may be NULL.
|
||||
/// \return #BADADDR if not, otherwise the address denoted by the name
|
||||
|
||||
idaman ea_t ida_export dummy_name_ea(const char *name);
|
||||
|
||||
|
||||
/// Extract a name or address from the specified string.
|
||||
/// \param[out] out output buffer for the identifier
|
||||
/// \param line input string
|
||||
/// \param x x coordinate of cursor
|
||||
/// \return -1 if cannot extract. otherwise length of the name
|
||||
|
||||
idaman ssize_t ida_export extract_name(qstring *out, const char *line, int x);
|
||||
|
||||
|
||||
/// Remove name from the list of names
|
||||
/// \param ea address of the name
|
||||
|
||||
idaman void ida_export hide_name(ea_t ea);
|
||||
|
||||
|
||||
/// Insert name to the list of names
|
||||
|
||||
idaman void ida_export show_name(ea_t ea);
|
||||
|
||||
|
||||
/// Get address of the name.
|
||||
/// Dummy names (like byte_xxxx where xxxx are hex digits) are parsed by this
|
||||
/// function to obtain the address. The database is not consulted for them.
|
||||
/// This function works only with regular names.
|
||||
/// \param from linear address where the name is used.
|
||||
/// if not applicable, then should be #BADADDR.
|
||||
/// \param name any name in the program or NULL
|
||||
/// \return address of the name or #BADADDR
|
||||
|
||||
idaman ea_t ida_export get_name_ea(ea_t from, const char *name);
|
||||
|
||||
|
||||
/// Get address of the name used in the expression for the address
|
||||
/// \param from address of the operand which references to the address
|
||||
/// \param to the referenced address
|
||||
/// \return address of the name used to represent the operand
|
||||
|
||||
idaman ea_t ida_export get_name_base_ea(ea_t from, ea_t to);
|
||||
|
||||
|
||||
/// Get value of the name.
|
||||
/// This function knows about: regular names, enums, special segments, etc.
|
||||
/// \param[out] value pointer to variable with answer
|
||||
/// \param from linear address where the name is used
|
||||
/// if not applicable, then should be BADADDR
|
||||
/// \param name any name in the program or NULL
|
||||
/// \return \ref NT_
|
||||
|
||||
idaman int ida_export get_name_value(uval_t *value, ea_t from, const char *name);
|
||||
|
||||
/// \defgroup NT_ Name value result codes
|
||||
/// Return values for get_name_value()
|
||||
//@{
|
||||
#define NT_NONE 0 ///< name doesn't exist or has no value
|
||||
#define NT_BYTE 1 ///< name is byte name (regular name)
|
||||
#define NT_LOCAL 2 ///< name is local label
|
||||
#define NT_STKVAR 3 ///< name is stack variable name
|
||||
#define NT_ENUM 4 ///< name is symbolic constant
|
||||
#define NT_ABS 5 ///< name is absolute symbol (#SEG_ABSSYM)
|
||||
#define NT_SEG 6 ///< name is segment or segment register name
|
||||
#define NT_STROFF 7 ///< name is structure member
|
||||
#define NT_BMASK 8 ///< name is a bit group mask name
|
||||
#define NT_REGVAR 9 ///< name is a renamed register (*value is idx into pfn->regvars)
|
||||
//@}
|
||||
|
||||
|
||||
/// Additional information for get_ea_name() function
|
||||
struct getname_info_t
|
||||
{
|
||||
size_t cb; ///< size of this struct
|
||||
int32 inhibitor; ///< codes to inhibit parts of demangled name (see \ref MNG_).
|
||||
///< Usually this is one of \inf{short_demnames} or \inf{long_demnames}.
|
||||
int32 demform; ///< demangle only if \inf{demnames} is equal to 'demform'.
|
||||
int32 demcode; ///< out: return value of demangler
|
||||
getname_info_t(void) : cb(sizeof(*this)), inhibitor(0), demform(0), demcode(0) {}
|
||||
};
|
||||
|
||||
|
||||
/// Get name at the specified address.
|
||||
/// \param[out] out buffer to hold the name
|
||||
/// \param ea linear address
|
||||
/// \param gtn_flags how exactly the name should be retrieved.
|
||||
/// combination of \ref GN_ bits
|
||||
/// \param gtni additional information for name demangling
|
||||
/// Please use the convenience functions declared below instead of calling
|
||||
/// get_ea_name directly.
|
||||
/// \return success
|
||||
|
||||
idaman ssize_t ida_export get_ea_name(
|
||||
qstring *out,
|
||||
ea_t ea,
|
||||
int gtn_flags=0,
|
||||
getname_info_t *gtni=NULL);
|
||||
|
||||
/// \defgroup GN_ bits for get_ea_name() function. There is a convenience
|
||||
/// function calc_gtn_flags() to calculate the GN_LOCAL flag
|
||||
//@{
|
||||
#define GN_VISIBLE 0x0001 ///< replace forbidden characters by SUBSTCHAR
|
||||
#define GN_COLORED 0x0002 ///< return colored name
|
||||
#define GN_DEMANGLED 0x0004 ///< return demangled name
|
||||
#define GN_STRICT 0x0008 ///< fail if cannot demangle
|
||||
#define GN_SHORT 0x0010 ///< use short form of demangled name
|
||||
#define GN_LONG 0x0020 ///< use long form of demangled name
|
||||
#define GN_LOCAL 0x0040 ///< try to get local name first; if failed, get global
|
||||
#define GN_ISRET 0x0080 ///< for dummy names: use retloc
|
||||
#define GN_NOT_ISRET 0x0100 ///< for dummy names: do not use retloc
|
||||
#define GN_NOT_DUMMY 0x0200 ///< do not return a dummy name
|
||||
|
||||
//@}
|
||||
|
||||
// Convenience functions for get_ea_name returning ssize_t
|
||||
|
||||
inline ssize_t get_name(qstring *out, ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
return get_ea_name(out, ea, gtn_flags);
|
||||
}
|
||||
|
||||
inline ssize_t idaapi get_visible_name(qstring *out, ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
return get_ea_name(out, ea, GN_VISIBLE|gtn_flags);
|
||||
}
|
||||
|
||||
inline ssize_t idaapi get_colored_name(qstring *out, ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
return get_ea_name(out, ea, GN_VISIBLE|GN_COLORED|gtn_flags);
|
||||
}
|
||||
|
||||
inline ssize_t idaapi get_short_name(qstring *out, ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
return get_ea_name(out, ea, GN_VISIBLE|GN_DEMANGLED|GN_SHORT|gtn_flags);
|
||||
}
|
||||
|
||||
inline ssize_t idaapi get_long_name(qstring *out, ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
return get_ea_name(out, ea, GN_VISIBLE|GN_DEMANGLED|GN_LONG|gtn_flags);
|
||||
}
|
||||
|
||||
inline ssize_t idaapi get_colored_short_name(qstring *out, ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
return get_ea_name(out, ea, GN_VISIBLE|GN_COLORED|GN_DEMANGLED|GN_SHORT|gtn_flags);
|
||||
}
|
||||
|
||||
inline ssize_t idaapi get_colored_long_name(qstring *out, ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
return get_ea_name(out, ea, GN_VISIBLE|GN_COLORED|GN_DEMANGLED|GN_LONG|gtn_flags);
|
||||
}
|
||||
|
||||
inline ssize_t idaapi get_demangled_name(
|
||||
qstring *out,
|
||||
ea_t ea,
|
||||
int32 inhibitor,
|
||||
int demform,
|
||||
int gtn_flags=0)
|
||||
{
|
||||
getname_info_t gtni;
|
||||
gtni.inhibitor = inhibitor;
|
||||
gtni.demform = demform;
|
||||
gtn_flags |= GN_VISIBLE | GN_DEMANGLED;
|
||||
return get_ea_name(out, ea, gtn_flags, >ni);
|
||||
}
|
||||
|
||||
inline ssize_t idaapi get_colored_demangled_name(
|
||||
qstring *out,
|
||||
ea_t ea,
|
||||
int32 inhibitor,
|
||||
int demform,
|
||||
int gtn_flags=0)
|
||||
{
|
||||
return get_demangled_name(out, ea, inhibitor, demform, gtn_flags|GN_COLORED);
|
||||
}
|
||||
|
||||
// Convenience functions for get_ea_name returning qstring
|
||||
|
||||
inline qstring get_name(ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
get_ea_name(&out, ea, gtn_flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline qstring get_visible_name(ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
get_ea_name(&out, ea, GN_VISIBLE|gtn_flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline qstring idaapi get_colored_name(ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
get_ea_name(&out, ea, GN_VISIBLE|GN_COLORED|gtn_flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline qstring idaapi get_short_name(ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
get_ea_name(&out, ea, GN_VISIBLE|GN_DEMANGLED|GN_SHORT|gtn_flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline qstring idaapi get_long_name(ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
get_ea_name(&out, ea, GN_VISIBLE|GN_DEMANGLED|GN_LONG|gtn_flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline qstring idaapi get_colored_short_name(ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
get_ea_name(&out, ea, GN_VISIBLE|GN_COLORED|GN_DEMANGLED|GN_SHORT|gtn_flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline qstring idaapi get_colored_long_name(ea_t ea, int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
get_ea_name(&out, ea, GN_VISIBLE|GN_COLORED|GN_DEMANGLED|GN_LONG|gtn_flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline qstring idaapi get_demangled_name(
|
||||
ea_t ea,
|
||||
int32 inhibitor,
|
||||
int demform,
|
||||
int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
getname_info_t gtni;
|
||||
gtni.inhibitor = inhibitor;
|
||||
gtni.demform = demform;
|
||||
gtn_flags |= GN_VISIBLE | GN_DEMANGLED;
|
||||
get_ea_name(&out, ea, gtn_flags, >ni);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline qstring idaapi get_colored_demangled_name(
|
||||
ea_t ea,
|
||||
int32 inhibitor,
|
||||
int demform,
|
||||
int gtn_flags=0)
|
||||
{
|
||||
qstring out;
|
||||
get_demangled_name(&out, ea, inhibitor, demform, gtn_flags|GN_COLORED);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Calculate flags for get_ea_name() function
|
||||
#ifdef FUNCS_HPP
|
||||
inline int calc_gtn_flags(ea_t from, ea_t ea)
|
||||
{
|
||||
return func_contains(get_func(from), ea) ? GN_LOCAL : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Get name color.
|
||||
/// \param from linear address where the name is used.
|
||||
/// if not applicable, then should be #BADADDR.
|
||||
/// The kernel returns a local name color if the reference is
|
||||
/// within a function, i.e. 'from' and 'ea' belong to the same function.
|
||||
/// \param ea linear address
|
||||
|
||||
idaman color_t ida_export get_name_color(ea_t from, ea_t ea);
|
||||
|
||||
|
||||
/// \defgroup GETN_ Name expression flags
|
||||
/// Passed as 'flags' parameter to get_name_expr()
|
||||
//@{
|
||||
#define GETN_APPZERO 0x0001 ///< meaningful only if the name refers to a structure.
|
||||
///< append a struct field name if the field offset is zero?
|
||||
#define GETN_NOFIXUP 0x0002 ///< ignore the fixup information when producing the name
|
||||
#define GETN_NODUMMY 0x0004 ///< do not create a new dummy name but pretend it exists
|
||||
//@}
|
||||
|
||||
/// Convert address to name expression (name with a displacement).
|
||||
/// This function takes into account fixup information and returns
|
||||
/// a colored name expression (in the form <name> +/- <offset>).
|
||||
/// It also knows about structure members and arrays.
|
||||
/// If the specified address doesn't have a name, a dummy name is generated.
|
||||
/// \param[out] out output buffer for the name
|
||||
/// \param from linear address of instruction operand or data referring to
|
||||
/// the name. This address will be used to get fixup information,
|
||||
/// so it should point to exact position of the operand in the
|
||||
/// instruction.
|
||||
/// \param n number of referencing operand. for data items specify 0
|
||||
/// \param ea address to convert to name expression
|
||||
/// \param off the value of name expression. this parameter is used only to
|
||||
/// check that the name expression will have the wanted value.
|
||||
/// 'off' may be equal to BADADDR but this is discouraged
|
||||
/// because it prohibits checks.
|
||||
/// \param flags \ref GETN_
|
||||
/// \return < 0 if address is not valid, no segment or other failure.
|
||||
/// otherwise the length of the name expression in characters.
|
||||
|
||||
idaman ssize_t ida_export get_name_expr(
|
||||
qstring *out,
|
||||
ea_t from,
|
||||
int n,
|
||||
ea_t ea,
|
||||
uval_t off,
|
||||
int flags=GETN_APPZERO);
|
||||
|
||||
/// Get a nice colored name at the specified address.
|
||||
/// Ex:
|
||||
/// - segment:sub+offset
|
||||
/// - segment:sub:local_label
|
||||
/// - segment:label
|
||||
/// - segment:address
|
||||
/// - segment:address+offset
|
||||
/// \param[out] buf buffer to hold the name
|
||||
/// \param ea linear address
|
||||
/// \param flags \ref GNCN_
|
||||
/// \return the length of the generated name in bytes.
|
||||
|
||||
idaman ssize_t ida_export get_nice_colored_name(
|
||||
qstring *buf,
|
||||
ea_t ea,
|
||||
int flags=0);
|
||||
|
||||
/// \defgroup GNCN_ Nice colored name flags
|
||||
/// Passed as 'flags' parameter to get_nice_colored_name()
|
||||
//@{
|
||||
#define GNCN_NOSEG 0x0001 ///< ignore the segment prefix when producing the name
|
||||
#define GNCN_NOCOLOR 0x0002 ///< generate an uncolored name
|
||||
#define GNCN_NOLABEL 0x0004 ///< don't generate labels
|
||||
#define GNCN_NOFUNC 0x0008 ///< don't generate funcname+... expressions
|
||||
#define GNCN_SEG_FUNC 0x0010 ///< generate both segment and function names (default is to omit segment name if a function name is present)
|
||||
#define GNCN_SEGNUM 0x0020 ///< segment part is displayed as a hex number
|
||||
#define GNCN_REQFUNC 0x0040 ///< return 0 if the address does not belong to a function
|
||||
#define GNCN_REQNAME 0x0080 ///< return 0 if the address can only be represented as a hex number
|
||||
#define GNCN_NODBGNM 0x0100 ///< don't use debug names
|
||||
#define GNCN_PREFDBG 0x0200 ///< if using debug names, prefer debug names over function names
|
||||
//@}
|
||||
|
||||
|
||||
/// Append names of struct fields to a name if the name is a struct name.
|
||||
/// \param out pointer to the output buffer
|
||||
/// \param disp displacement from the name
|
||||
/// \param n number of operand n which the name appears
|
||||
/// \param path path in the struct. path is an array of id's.
|
||||
/// maximal length of array is #MAXSTRUCPATH.
|
||||
/// the first element of the array is the structure id.
|
||||
/// consecutive elements are id's of used union members (if any).
|
||||
/// \param plen size of path array
|
||||
/// \param flags the input flags. they will be returned if the struct
|
||||
/// cannot be found.
|
||||
/// \param delta delta to add to displacement
|
||||
/// \param appzero should append a struct field name if the displacement is zero?
|
||||
/// \return flags of the innermost struct member or the input flags
|
||||
|
||||
idaman flags_t ida_export append_struct_fields(
|
||||
qstring *out,
|
||||
adiff_t *disp,
|
||||
int n,
|
||||
const tid_t *path,
|
||||
int plen,
|
||||
flags_t flags,
|
||||
adiff_t delta,
|
||||
bool appzero);
|
||||
|
||||
|
||||
/// Get offset within a structure if the operand refers to structure.
|
||||
/// Ex:
|
||||
/// \v{mov ax, somedata.field5-2 (before it was max ax, 3)}
|
||||
/// for this instruction, op #1 the function will return
|
||||
/// - disp: the value of 'field5', i.e. 5
|
||||
/// - delta: -2
|
||||
/// - path: the existing path if any
|
||||
/// \param disp pointer to displacement (answer will be here)
|
||||
/// \param delta pointer to displacement delta (answer will be here)
|
||||
/// \param path existing strpath (if any)
|
||||
/// \param ea linear address of instruction/data
|
||||
/// \param n number of operand
|
||||
/// \return if success, then length of path + 1.
|
||||
/// if failed, then 0.
|
||||
|
||||
idaman int ida_export get_struct_operand(
|
||||
adiff_t *disp,
|
||||
adiff_t *delta,
|
||||
tid_t *path,
|
||||
ea_t ea,
|
||||
int n);
|
||||
|
||||
|
||||
/// \name Work with publicness of a name
|
||||
//@{
|
||||
idaman bool ida_export is_public_name(ea_t ea);
|
||||
idaman void ida_export make_name_public(ea_t ea);
|
||||
idaman void ida_export make_name_non_public(ea_t ea);
|
||||
//@}
|
||||
|
||||
|
||||
/// \name Work with weak names.
|
||||
//@{
|
||||
idaman bool ida_export is_weak_name(ea_t ea);
|
||||
idaman void ida_export make_name_weak(ea_t ea);
|
||||
idaman void ida_export make_name_non_weak(ea_t ea);
|
||||
//@}
|
||||
|
||||
/// \name Work with the list of names
|
||||
//@{
|
||||
|
||||
/// Get number of names in the list
|
||||
|
||||
idaman size_t ida_export get_nlist_size(void);
|
||||
|
||||
/// Get index of the name in the list
|
||||
/// \warning returns the closest match.
|
||||
/// may return idx >= size.
|
||||
|
||||
idaman size_t ida_export get_nlist_idx(ea_t ea);
|
||||
|
||||
/// Is name included into names list?
|
||||
|
||||
idaman bool ida_export is_in_nlist(ea_t ea);
|
||||
|
||||
/// Get address from the list at 'idx'
|
||||
|
||||
idaman ea_t ida_export get_nlist_ea(size_t idx);
|
||||
|
||||
/// Get name using idx
|
||||
|
||||
idaman const char *ida_export get_nlist_name(size_t idx);
|
||||
|
||||
/// Rebuild names list
|
||||
|
||||
idaman void ida_export rebuild_nlist(void);
|
||||
//@}
|
||||
|
||||
/// Renumber dummy names
|
||||
|
||||
idaman void ida_export reorder_dummy_names(void);
|
||||
|
||||
/// Specify strategy for retrieving debug names
|
||||
enum debug_name_how_t
|
||||
{
|
||||
DEBNAME_EXACT, ///< find a name at exactly the specified address
|
||||
DEBNAME_LOWER, ///< find a name with the address >= the specified address
|
||||
DEBNAME_UPPER, ///< find a name with the address > the specified address
|
||||
DEBNAME_NICE, ///< find a name with the address <= the specified address
|
||||
};
|
||||
|
||||
/// ea, name pair
|
||||
struct ea_name_t
|
||||
{
|
||||
ea_t ea;
|
||||
qstring name;
|
||||
ea_name_t(void) : ea(BADADDR) {}
|
||||
ea_name_t(ea_t _ea, const qstring &_name) : ea(_ea), name(_name) {}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(ea_name_t);
|
||||
typedef qvector<ea_name_t> ea_name_vec_t; ///< vector of ea,name pairs
|
||||
|
||||
/// \name Debug names
|
||||
/// Debug names exist during the debugging session.
|
||||
/// The kernel does not verify them for anything and happily accepts
|
||||
/// any string as a name.
|
||||
//@{
|
||||
idaman int ida_export set_debug_names(const ea_t *addrs, const char *const *names, int qty);
|
||||
idaman bool ida_export set_debug_name(ea_t ea, const char *name);
|
||||
idaman ssize_t ida_export get_debug_name(
|
||||
qstring *out,
|
||||
ea_t *ea_ptr,
|
||||
debug_name_how_t how);
|
||||
idaman void ida_export del_debug_names(ea_t ea1, ea_t ea2);
|
||||
idaman ea_t ida_export get_debug_name_ea(const char *name);
|
||||
idaman void ida_export get_debug_names(ea_name_vec_t *names, ea_t ea1, ea_t ea2);
|
||||
//@}
|
||||
|
||||
|
||||
enum demreq_type_t
|
||||
{
|
||||
DQT_NPURGED_8 = -8, // only calculate number of purged bytes (sizeof(arg)==8)
|
||||
DQT_NPURGED_4 = -4, // only calculate number of purged bytes (sizeof(arg)==4)
|
||||
DQT_NPURGED_2 = -2, // only calculate number of purged bytes (sizeof(arg)==2)
|
||||
DQT_COMPILER = 0, // only detect compiler that generated the name
|
||||
DQT_NAME_TYPE = 1, // only detect the name type (data/code)
|
||||
DQT_FULL = 2, // really demangle
|
||||
};
|
||||
|
||||
/// Demangle a name.
|
||||
/// \param out output buffer
|
||||
/// \param name name to demangle
|
||||
/// \param disable_mask bits to inhibit parts of demangled name (see \ref MNG_).
|
||||
/// by the M_COMPILER bits a specific compiler can be
|
||||
/// selected (see \ref MT_).
|
||||
/// \return ME_... or MT__ bitmasks from demangle.hpp
|
||||
|
||||
idaman int32 ida_export demangle_name(
|
||||
qstring *out,
|
||||
const char *name,
|
||||
uint32 disable_mask,
|
||||
demreq_type_t demreq=DQT_FULL);
|
||||
|
||||
/// Demangle a name.
|
||||
inline qstring idaapi demangle_name(
|
||||
const char *name,
|
||||
uint32 disable_mask,
|
||||
demreq_type_t demreq=DQT_FULL)
|
||||
{
|
||||
qstring out;
|
||||
demangle_name(&out, name, disable_mask, demreq);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
inline int32 detect_compiler_using_demangler(const char *name)
|
||||
{
|
||||
return demangle_name(NULL, name, 0, DQT_COMPILER);
|
||||
}
|
||||
|
||||
/// What name types to ignore
|
||||
typedef int ignore_name_def_t;
|
||||
const ignore_name_def_t
|
||||
ignore_none = 0,
|
||||
ignore_regvar = 1,
|
||||
ignore_llabel = 2,
|
||||
ignore_stkvar = 3,
|
||||
ignore_glabel = 4;
|
||||
|
||||
/// Is the name defined locally in the specified function?
|
||||
/// \param pfn pointer to function
|
||||
/// \param name name to check
|
||||
/// \param ignore_name_def which names to ignore when checking
|
||||
/// \param ea1 the starting address of the range inside the function (optional)
|
||||
/// \param ea2 the ending address of the range inside the function (optional)
|
||||
/// \return true if the name has been defined
|
||||
idaman bool ida_export is_name_defined_locally(
|
||||
func_t *pfn,
|
||||
const char *name,
|
||||
ignore_name_def_t ignore_name_def,
|
||||
ea_t ea1=BADADDR,
|
||||
ea_t ea2=BADADDR);
|
||||
|
||||
// Clean a name.
|
||||
// This function removes punctuation marks (underscores and dots) from both
|
||||
// ends of the name, and other typical prefixes/suffixes. Name is assumed to
|
||||
// have the following format: [j_][@][.][_*][imp_]name[@digits][_NN]
|
||||
// \param out output buffer
|
||||
// \param ea address of the name (used to remove the module name)
|
||||
// if != BADADDR, the optional prefix (module name) will be
|
||||
// removed
|
||||
// \param name name to clean
|
||||
// \param flags combination of CN_... bits
|
||||
// \return true if returned a non-empty name
|
||||
idaman bool ida_export cleanup_name(
|
||||
qstring *out,
|
||||
ea_t ea,
|
||||
const char *name,
|
||||
uint32 flags=0);
|
||||
|
||||
#define CN_KEEP_TRAILING__DIGITS 0x01 // do not remove "_\d+" at the end of name
|
||||
|
||||
|
||||
#endif // _NAME_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,454 +0,0 @@
|
||||
#ifndef NETWORK_HPP
|
||||
#define NETWORK_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <pro.h>
|
||||
|
||||
#ifdef __NT__
|
||||
# if !defined(AF_MAX)
|
||||
# include <ws2tcpip.h>
|
||||
# endif
|
||||
# define SYSTEM "Windows"
|
||||
# define socklen_t int
|
||||
# define SHUT_RD SD_RECEIVE
|
||||
# define SHUT_WR SD_SEND
|
||||
# define SHUT_RDWR SD_BOTH
|
||||
#else // not NT, i.e. UNIX
|
||||
# include <netinet/in.h>
|
||||
# include <netinet/tcp.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <netdb.h>
|
||||
# define closesocket(s) close(s)
|
||||
# define SOCKET size_t
|
||||
# define INVALID_SOCKET size_t(-1)
|
||||
# define SOCKET_ERROR (-1)
|
||||
# if defined(__LINUX__)
|
||||
# if defined(__ARM__)
|
||||
# if defined(__ANDROID__)
|
||||
# define SYSTEM "Android"
|
||||
# else
|
||||
# define SYSTEM "ARM Linux"
|
||||
# endif
|
||||
# else
|
||||
# if defined(__ANDROID__)
|
||||
# define SYSTEM "Android x86"
|
||||
# else
|
||||
# define SYSTEM "Linux"
|
||||
# endif
|
||||
# endif
|
||||
// linux debugger cannot be multithreaded because it uses thread_db.
|
||||
// i doubt that this library is meant to be used with multiple
|
||||
// applications simultaneously.
|
||||
# define __SINGLE_THREADED_SERVER__
|
||||
# elif defined(__MAC__)
|
||||
# define SYSTEM "Mac OS X"
|
||||
# else
|
||||
# error "Unknown platform"
|
||||
# endif
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#ifndef __X86__
|
||||
# define _SYSBITS " 64-bit"
|
||||
#else
|
||||
# define _SYSBITS " 32-bit"
|
||||
#endif
|
||||
|
||||
#ifdef TESTABLE_BUILD
|
||||
# ifdef __EA64__
|
||||
# define SYSBITS _SYSBITS " (sizeof ea=64)"
|
||||
# else
|
||||
# define SYSBITS _SYSBITS " (sizeof ea=32)"
|
||||
# endif
|
||||
#else
|
||||
# define SYSBITS _SYSBITS
|
||||
#endif
|
||||
|
||||
#ifdef __SINGLE_THREADED_SERVER__
|
||||
# define __SERVER_TYPE__ "ST"
|
||||
#else
|
||||
# define __SERVER_TYPE__ "MT"
|
||||
#endif
|
||||
|
||||
#define TIMEOUT (1000/25) // timeout for polling (ms)
|
||||
#define TIMEOUT_INFINITY -1
|
||||
#define RECV_HELLO_TIMEOUT 1000 // timeout for the first packet (ms)
|
||||
#define RECV_TIMEOUT_PERIOD 10000 // timeout for recv (ms)
|
||||
|
||||
// bidirectional codes (client <-> server)
|
||||
enum base_packet_id_t
|
||||
{
|
||||
RPC_OK = 0, // response: function call succeeded
|
||||
RPC_UNK, // response: unknown function code
|
||||
RPC_MEM, // response: no memory
|
||||
base_packet_id_last
|
||||
};
|
||||
|
||||
#define RPC_OPEN 3 // server->client: i'm ready, the very first packet
|
||||
|
||||
#define RPC_EVENT 4 // server->client: debug event ready, followed by debug_event
|
||||
#define RPC_EVOK 5 // client->server: event processed (in response to RPC_EVENT)
|
||||
#define RPC_CANCELLED 6 // client->server: operation was cancelled by the user
|
||||
// we need EVOK to handle the situation when the debug
|
||||
// event was detected by the server during polling and
|
||||
// was sent to the client using RPC_EVENT but client has not received it yet
|
||||
// and requested GET_DEBUG_EVENT. In this case we should not
|
||||
// call remote_get_debug_event() but instead force the client
|
||||
// to use the event sent by RPC_EVENT.
|
||||
// In other words, if the server has sent RPC_EVENT but has not
|
||||
// received RPC_EVOK, it should fail all GET_DEBUG_EVENTS.
|
||||
|
||||
// client->server codes
|
||||
#define RPC_INIT 10
|
||||
#define RPC_TERM 11
|
||||
#define RPC_GET_PROCESSES 12
|
||||
#define RPC_START_PROCESS 13
|
||||
#define RPC_EXIT_PROCESS 14
|
||||
#define RPC_ATTACH_PROCESS 15
|
||||
#define RPC_DETACH_PROCESS 16
|
||||
#define RPC_GET_DEBUG_EVENT 17
|
||||
#define RPC_PREPARE_TO_PAUSE_PROCESS 18
|
||||
#define RPC_STOPPED_AT_DEBUG_EVENT 19
|
||||
#define RPC_CONTINUE_AFTER_EVENT 20
|
||||
#define RPC_TH_SUSPEND 21
|
||||
#define RPC_TH_CONTINUE 22
|
||||
#define RPC_SET_RESUME_MODE 23
|
||||
#define RPC_GET_MEMORY_INFO 24
|
||||
#define RPC_READ_MEMORY 25
|
||||
#define RPC_WRITE_MEMORY 26
|
||||
#define RPC_UPDATE_BPTS 27
|
||||
#define RPC_UPDATE_LOWCNDS 28
|
||||
#define RPC_EVAL_LOWCND 29
|
||||
#define RPC_ISOK_BPT 30
|
||||
#define RPC_READ_REGS 31
|
||||
#define RPC_WRITE_REG 32
|
||||
#define RPC_GET_SREG_BASE 33
|
||||
#define RPC_SET_EXCEPTION_INFO 34
|
||||
|
||||
#define RPC_OPEN_FILE 35
|
||||
#define RPC_CLOSE_FILE 36
|
||||
#define RPC_READ_FILE 37
|
||||
#define RPC_WRITE_FILE 38
|
||||
#define RPC_IOCTL 39 // both client and the server may send this packet
|
||||
#define RPC_UPDATE_CALL_STACK 40
|
||||
#define RPC_APPCALL 41
|
||||
#define RPC_CLEANUP_APPCALL 42
|
||||
#define RPC_REXEC 43
|
||||
#define RPC_GET_SCATTERED_IMAGE 44
|
||||
#define RPC_GET_IMAGE_UUID 45
|
||||
#define RPC_GET_SEGM_START 46
|
||||
#define RPC_BIN_SEARCH 47
|
||||
|
||||
// server->client codes
|
||||
#define RPC_SET_DEBUG_NAMES 50
|
||||
#define RPC_SYNC_STUB 51
|
||||
#define RPC_ERROR 52
|
||||
#define RPC_MSG 53
|
||||
#define RPC_WARNING 54
|
||||
#define RPC_HANDLE_DEBUG_EVENT 55
|
||||
#define RPC_REPORT_IDC_ERROR 56
|
||||
#define RPC_IMPORT_DLL 57
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct PACKED rpc_packet_t
|
||||
{ // fields are always sent in the network order
|
||||
uint32 length; // length of the packet (do not count length & code)
|
||||
uchar code; // function code
|
||||
};
|
||||
CASSERT(sizeof(rpc_packet_t) == 5);
|
||||
#pragma pack(pop)
|
||||
|
||||
enum rpc_notification_type_t
|
||||
{
|
||||
rnt_unknown = 0,
|
||||
rnt_msg,
|
||||
rnt_warning,
|
||||
rnt_error,
|
||||
};
|
||||
|
||||
#define DEFINE_ONE_NOTIFICATION_FUNCTION(FuncName, NotifCode, RpcEngineInst) \
|
||||
AS_PRINTF(2, 3) void FuncName(const char *format, ...) \
|
||||
{ \
|
||||
va_list va; \
|
||||
va_start(va, format); \
|
||||
dvnotif(NotifCode, RpcEngineInst, format, va); \
|
||||
va_end(va); \
|
||||
}
|
||||
|
||||
#define DEFINE_ALL_NOTIFICATION_FUNCTIONS(RpcEngineInst) \
|
||||
DEFINE_ONE_NOTIFICATION_FUNCTION(dmsg, 0, RpcEngineInst) \
|
||||
DEFINE_ONE_NOTIFICATION_FUNCTION(dwarning, 1, RpcEngineInst) \
|
||||
DEFINE_ONE_NOTIFICATION_FUNCTION(derror, -1, RpcEngineInst)
|
||||
|
||||
class rpc_engine_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
AS_PRINTF(2, 0) ssize_t dvnotif_client(
|
||||
int code,
|
||||
const char *format,
|
||||
va_list va);
|
||||
|
||||
#ifdef __NT__
|
||||
# define IRSERR_TIMEOUT WAIT_TIMEOUT
|
||||
#else
|
||||
# define IRSERR_TIMEOUT ETIME
|
||||
#endif
|
||||
#define IRSERR_CANCELLED -0xE5CA7E // escape
|
||||
#define IRSERR_SKIP_ITER -0x5217 // skip recv() in rpc_engine_t's recv_data loop
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// idarpc_stream_t
|
||||
//-------------------------------------------------------------------------
|
||||
// the idarpc_stream_t structure is not defined.
|
||||
// it is used as an opaque type provided by the transport level.
|
||||
// the transport level defines its own local type for it.
|
||||
struct idarpc_stream_t;
|
||||
|
||||
idarpc_stream_t *irs_new(bool use_tls=false);
|
||||
bool irs_init_client(idarpc_stream_t *irs, const char *hostname, int port_number);
|
||||
bool irs_init_server(
|
||||
idarpc_stream_t *irs,
|
||||
const char *hostname,
|
||||
int port_number,
|
||||
const char *certchain=nullptr,
|
||||
const char *privkey=nullptr);
|
||||
bool irs_accept(idarpc_stream_t *irs, idarpc_stream_t *listener);
|
||||
bool irs_handshake(idarpc_stream_t *irs, int timeout_ms = -1);
|
||||
int irs_ready(idarpc_stream_t *irs, int timeout_ms = -1);
|
||||
ssize_t irs_recv(idarpc_stream_t *irs, void *buf, size_t n);
|
||||
ssize_t irs_send(idarpc_stream_t *irs, const void *buf, size_t n);
|
||||
void irs_term(idarpc_stream_t **pirs, int shutdown_flags = -1);
|
||||
int irs_get_error(idarpc_stream_t *irs);
|
||||
const char *irs_strerror(idarpc_stream_t *irs);
|
||||
bool irs_peername(idarpc_stream_t *irs, qstring *out, bool lookupname = true);
|
||||
bool irs_sockname(idarpc_stream_t *irs, qstring *out, bool lookupname = true);
|
||||
|
||||
enum progress_loop_ctrl_t
|
||||
{
|
||||
plc_proceed,
|
||||
plc_skip_iter,
|
||||
plc_cancel,
|
||||
};
|
||||
typedef progress_loop_ctrl_t irs_progress_cb_t(bool receiving, size_t processed, size_t total, void *);
|
||||
void irs_set_progress_cb(idarpc_stream_t *irs, int ms, irs_progress_cb_t cb, void *ud=NULL);
|
||||
struct irs_cancellable_op_t
|
||||
{
|
||||
idarpc_stream_t *irs;
|
||||
irs_cancellable_op_t(idarpc_stream_t *_irs, bool receiving, size_t goal=0);
|
||||
~irs_cancellable_op_t();
|
||||
void inc_progress(size_t progress);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
typedef qtime64_t utc_timestamp_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// base_dispatcher_t + client_handler_t
|
||||
//-------------------------------------------------------------------------
|
||||
struct client_handler_t
|
||||
{
|
||||
FILE *channels[16];
|
||||
idarpc_stream_t *irs;
|
||||
qstring peer_name;
|
||||
uint32 session_id;
|
||||
utc_timestamp_t session_start;
|
||||
bool verbose;
|
||||
|
||||
void close_all_channels();
|
||||
void clear_channels();
|
||||
int find_free_channel() const;
|
||||
|
||||
client_handler_t(idarpc_stream_t *_irs, bool _verbose);
|
||||
virtual ~client_handler_t();
|
||||
|
||||
virtual bool handle() = 0; // true - delete this
|
||||
virtual void shutdown_gracefully(int signum) = 0;
|
||||
|
||||
//lint -sem(client_handler_t::term_irs,cleanup)
|
||||
void term_irs();
|
||||
|
||||
AS_PRINTF(2, 3) int lprintf(const char *format, ...) const;
|
||||
|
||||
private:
|
||||
DECLARE_UNCOPYABLE(client_handler_t);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct client_handlers_list_t
|
||||
{
|
||||
typedef std::map<client_handler_t *, qthread_t> storage_t;
|
||||
storage_t storage;
|
||||
|
||||
virtual ~client_handlers_list_t() {}
|
||||
virtual void lock() {}
|
||||
virtual void unlock() {}
|
||||
virtual bool is_multi_threaded() const { return false; }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct mt_client_handlers_list_t : public client_handlers_list_t
|
||||
{
|
||||
qmutex_t mutex;
|
||||
|
||||
mt_client_handlers_list_t() { mutex = qmutex_create(); QASSERT(1540, mutex != NULL); }
|
||||
virtual ~mt_client_handlers_list_t() { qmutex_free(mutex); }
|
||||
virtual void lock() override { qmutex_lock(mutex); }
|
||||
virtual void unlock() override { qmutex_unlock(mutex); }
|
||||
virtual bool is_multi_threaded() const override { return true; }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct base_dispatcher_t
|
||||
{
|
||||
ushort port_number;
|
||||
qstring ipv4_address;
|
||||
qstring certchain;
|
||||
qstring privkey;
|
||||
idarpc_stream_t *irs;
|
||||
client_handlers_list_t *clients_list;
|
||||
bool use_tls;
|
||||
bool verbose;
|
||||
|
||||
base_dispatcher_t(bool multi_threaded);// : port_number(-1), irs(NULL), verbose(false) {}
|
||||
virtual ~base_dispatcher_t();
|
||||
void dispatch();
|
||||
|
||||
virtual void collect_cliopts(cliopts_t *out);
|
||||
|
||||
//
|
||||
void install_signal_handlers();
|
||||
|
||||
//
|
||||
virtual client_handler_t *new_client_handler(idarpc_stream_t *_irs) = 0;
|
||||
void delete_client_handler(client_handler_t *inst);
|
||||
|
||||
virtual void shutdown_gracefully(int signum);
|
||||
|
||||
|
||||
private:
|
||||
void handle_session(client_handler_t *handler);
|
||||
void add_to_clients_list(client_handler_t *handler, qthread_t t);
|
||||
DECLARE_UNCOPYABLE(base_dispatcher_t);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// packing/unpacking utils
|
||||
//-------------------------------------------------------------------------
|
||||
bytevec_t prepare_rpc_packet(uchar code);
|
||||
void finalize_packet(bytevec_t &pkt);
|
||||
//const char *get_rpc_name(int code);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// rpc_engine_t
|
||||
//-------------------------------------------------------------------------
|
||||
#define VERBOSE_ENABLED
|
||||
#ifdef VERBOSE_ENABLED
|
||||
#define verb(x) do { if ( verbose ) msg x; } while(0)
|
||||
#define verb_eng(engine, x) do { if ( (engine)->verbose ) msg x; } while(0)
|
||||
#else
|
||||
#define verb(x) //msg x
|
||||
#define verb_eng(engine, x)
|
||||
#endif
|
||||
#define verbev(x) //msg x
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct rpc_packet_data_t
|
||||
{
|
||||
uchar code;
|
||||
|
||||
rpc_packet_data_t(uchar _code) : code(_code) {}
|
||||
virtual ~rpc_packet_data_t() {}
|
||||
virtual void serialize(bytevec_t *out, int version) const = 0;
|
||||
virtual bool deserialize(const uchar **ptr, size_t len, int version) = 0;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
typedef int ioctl_handler_t(
|
||||
class rpc_engine_t *rpc,
|
||||
int fn,
|
||||
const void *buf,
|
||||
size_t size,
|
||||
void **poutbuf,
|
||||
ssize_t *poutsize);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
typedef rpc_packet_data_t *rpc_packet_instantiator_t(const uchar *ptr, size_t len, int version);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct rpc_packet_type_desc_t
|
||||
{
|
||||
uchar code;
|
||||
const char *name;
|
||||
rpc_packet_instantiator_t *instantiate;
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(rpc_packet_type_desc_t);
|
||||
typedef qvector<rpc_packet_type_desc_t> rpc_packet_type_desc_vec_t;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
class rpc_engine_t
|
||||
{
|
||||
public:
|
||||
bool network_error;
|
||||
|
||||
// pointer to the ioctl request handler, in case you
|
||||
// need to handle ioctl requests from the server.
|
||||
ioctl_handler_t *ioctl_handler;
|
||||
int recv_timeout;
|
||||
bool is_client;
|
||||
bool logged_in;
|
||||
|
||||
protected:
|
||||
void register_packet_type_descs(const rpc_packet_type_desc_t *ptypes, size_t cnt);
|
||||
const rpc_packet_type_desc_t *find_packet_type_desc(int code) const;
|
||||
const rpc_packet_type_desc_t *find_packet_type_desc(const char *name) const;
|
||||
|
||||
public:
|
||||
rpc_engine_t(bool is_client);
|
||||
virtual ~rpc_engine_t() {}
|
||||
|
||||
int handle_ioctl_packet(bytevec_t &pkt, const uchar *ptr, const uchar *end);
|
||||
|
||||
// low-level: deal with bytes, and don't handle "conversations".
|
||||
int send_data(bytevec_t &data);
|
||||
rpc_packet_t *recv_packet();
|
||||
|
||||
virtual rpc_packet_t *send_request_and_receive_reply(bytevec_t &pkt) = 0;
|
||||
|
||||
virtual idarpc_stream_t *get_irs() const = 0;
|
||||
AS_PRINTF(3, 0) virtual ssize_t send_notif(int code, const char *format, va_list va);
|
||||
|
||||
virtual bool get_broken_connection(void) { return false; }
|
||||
virtual void set_broken_connection(void) {};
|
||||
|
||||
int send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize);
|
||||
void set_ioctl_handler(ioctl_handler_t *h) { ioctl_handler = h; }
|
||||
|
||||
DEFINE_ALL_NOTIFICATION_FUNCTIONS(this);
|
||||
|
||||
private:
|
||||
rpc_packet_type_desc_vec_t ptypes;
|
||||
|
||||
int recv_data(void *out, size_t len);
|
||||
|
||||
AS_PRINTF(3,0) static ssize_t dvnotif(int code, rpc_engine_t *rpc, const char *format, va_list va);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
AS_PRINTF(3, 0) ssize_t dvnotif_rpc(
|
||||
int code,
|
||||
rpc_engine_t *rpc,
|
||||
const char *format,
|
||||
va_list va);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
AS_PRINTF(1, 0) int vlprintf(const char *format, va_list va);
|
||||
AS_PRINTF(1, 2) int lprintf(const char *format, ...);
|
||||
void set_lprintf_output(FILE *out);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
size_t format_timestamp(char *buf, size_t bufsize, qtime64_t ts);
|
||||
|
||||
#endif // NETWORK_HPP
|
||||
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _OFFSET_HPP
|
||||
#define _OFFSET_HPP
|
||||
|
||||
#include <nalt.hpp>
|
||||
#include <segment.hpp>
|
||||
|
||||
/*! \file offset.hpp
|
||||
|
||||
\brief Functions that deal with offsets.
|
||||
|
||||
"Being an offset" is a characteristic of an operand.
|
||||
This means that operand or its part represent offset from
|
||||
some address in the program. This linear address is called
|
||||
"offset base". Some operands may have 2 offsets simultaneously.
|
||||
Generally, IDA doesn't handle this except for Motorola outer offsets.
|
||||
Thus there may be two offset values in an operand: simple offset and
|
||||
outer offset.
|
||||
|
||||
Outer offsets are handled by specifying special operand number:
|
||||
it should be ORed with #OPND_OUTER value.
|
||||
|
||||
See bytes.hpp for further explanation of operand numbers.
|
||||
*/
|
||||
|
||||
|
||||
/// Get default reference type depending on the segment.
|
||||
/// \return one of ::REF_OFF8,::REF_OFF16,::REF_OFF32
|
||||
|
||||
idaman reftype_t ida_export get_default_reftype(ea_t ea);
|
||||
|
||||
|
||||
/// Convert operand to a reference.
|
||||
/// To delete an offset, use clr_op_type() function.
|
||||
/// \param ea linear address.
|
||||
/// if 'ea' has unexplored bytes, try to convert them to
|
||||
/// - no segment: fail
|
||||
/// - 16bit segment: to 16bit word data
|
||||
/// - 32bit segment: to dword
|
||||
/// \param n number of operand (may be ORed with #OPND_OUTER)
|
||||
/// - 0: first
|
||||
/// - 1: second
|
||||
/// - 2: third
|
||||
/// - #OPND_MASK: all operands
|
||||
/// \param ri reference information
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export op_offset_ex(ea_t ea, int n, const refinfo_t *ri);
|
||||
|
||||
|
||||
/// See op_offset_ex()
|
||||
|
||||
idaman bool ida_export op_offset(
|
||||
ea_t ea,
|
||||
int n,
|
||||
reftype_t type,
|
||||
ea_t target=BADADDR,
|
||||
ea_t base=0,
|
||||
adiff_t tdelta=0);
|
||||
|
||||
|
||||
/// Convert operand to a reference with the default reference type
|
||||
|
||||
inline bool op_plain_offset(ea_t ea, int n, ea_t base)
|
||||
{
|
||||
reftype_t reftype = get_default_reftype(ea);
|
||||
return op_offset(ea, n, reftype, BADADDR, base) != 0;
|
||||
}
|
||||
|
||||
|
||||
/// Get offset base value
|
||||
/// \param ea linear address
|
||||
/// \param n number of operand
|
||||
/// \return offset base or #BADADDR
|
||||
|
||||
inline ea_t get_offbase(ea_t ea, int n)
|
||||
{
|
||||
refinfo_t ri;
|
||||
if ( !get_refinfo(&ri, ea, n) )
|
||||
return BADADDR;
|
||||
return ri.base;
|
||||
}
|
||||
|
||||
|
||||
/// Get offset expression (in the form "offset name+displ").
|
||||
/// This function uses offset translation function (\ph{translate}) if your IDP
|
||||
/// module has such a function. Translation function is used to map linear
|
||||
/// addresses in the program (only for offsets).
|
||||
///
|
||||
/// Example: suppose we have instruction at linear address 0x00011000:
|
||||
/// \v{mov ax, [bx+7422h]}
|
||||
/// and at ds:7422h:
|
||||
/// \v{array dw ...}
|
||||
/// We want to represent the second operand with an offset expression, so
|
||||
/// then we call:
|
||||
/// \v{
|
||||
/// get_offset_expresion(0x001100, 1, 0x001102, 0x7422, buf);
|
||||
/// | | | | |
|
||||
/// | | | | +output buffer
|
||||
/// | | | +value of offset expression
|
||||
/// | | +address offset value in the instruction
|
||||
/// | +the second operand
|
||||
/// +address of instruction
|
||||
/// }
|
||||
/// and the function will return a colored string:
|
||||
/// \v{offset array}
|
||||
/// \param buf output buffer to hold offset expression
|
||||
/// \param ea start of instruction or data with the offset expression
|
||||
/// \param n number of operand (may be ORed with #OPND_OUTER)
|
||||
/// - 0: first operand
|
||||
/// - 1: second operand
|
||||
/// \param from linear address of instruction operand or data referring to
|
||||
/// the name. This address will be used to get fixup information,
|
||||
/// so it should point to exact position of operand in the
|
||||
/// instruction.
|
||||
/// \param offset value of operand or its part. The function will return
|
||||
/// text representation of this value as offset expression.
|
||||
/// \param getn_flags combination of:
|
||||
/// - #GETN_APPZERO: meaningful only if the name refers to
|
||||
/// a structure. appends the struct field name
|
||||
/// if the field offset is zero
|
||||
/// - #GETN_NODUMMY: do not generate dummy names for the expression
|
||||
/// but pretend they already exist
|
||||
/// (useful to verify that the offset expression
|
||||
/// can be represented)
|
||||
/// \retval 0 can't convert to offset expression
|
||||
/// \retval 1 ok, a simple offset expression
|
||||
/// \retval 2 ok, a complex offset expression
|
||||
|
||||
|
||||
idaman int ida_export get_offset_expression(
|
||||
qstring *buf,
|
||||
ea_t ea,
|
||||
int n,
|
||||
ea_t from,
|
||||
adiff_t offset,
|
||||
int getn_flags=0);
|
||||
|
||||
|
||||
/// See get_offset_expression()
|
||||
|
||||
idaman int ida_export get_offset_expr(
|
||||
qstring *buf,
|
||||
ea_t ea,
|
||||
int n,
|
||||
const refinfo_t &ri,
|
||||
ea_t from,
|
||||
adiff_t offset,
|
||||
int getn_flags=0);
|
||||
|
||||
|
||||
/// Does the specified address contain a valid OFF32 value?.
|
||||
/// For symbols in special segments the displacement is not taken into account.
|
||||
/// If yes, then the target address of OFF32 will be returned.
|
||||
/// If not, then #BADADDR is returned.
|
||||
|
||||
idaman ea_t ida_export can_be_off32(ea_t ea);
|
||||
|
||||
|
||||
/// Try to calculate the offset base
|
||||
/// This function takes into account the fixup information,
|
||||
/// current ds and cs values.
|
||||
/// \param ea the referencing instruction/data address
|
||||
/// \param n operand number
|
||||
/// - 0: first operand
|
||||
/// - 1: other operand
|
||||
/// \return output base address or #BADADDR
|
||||
|
||||
idaman ea_t ida_export calc_offset_base(ea_t ea, int n);
|
||||
|
||||
|
||||
/// Try to calculate the offset base.
|
||||
/// 2 bases are checked: current ds and cs.
|
||||
/// If fails, return #BADADDR
|
||||
|
||||
idaman ea_t ida_export calc_probable_base_by_value(ea_t ea, uval_t off);
|
||||
|
||||
|
||||
/// Calculate the target and base addresses of an offset expression.
|
||||
/// The calculated target and base addresses are returned in the locations
|
||||
/// pointed by 'base' and 'target'. In case 'ri.base' is #BADADDR, the
|
||||
/// function calculates the offset base address from the referencing
|
||||
/// instruction/data address.
|
||||
/// The target address is copied from ri.target. If ri.target is #BADADDR
|
||||
/// then the target is calculated using the base address and 'opval'.
|
||||
/// This function also checks if 'opval' matches the full value of the
|
||||
/// reference and takes in account the memory-mapping.
|
||||
/// \param target output target address
|
||||
/// \param base output base address
|
||||
/// \param from the referencing instruction/data address
|
||||
/// \param ri reference info block from the database
|
||||
/// \param opval operand value (usually op_t::value or op_t::addr)
|
||||
/// \return success
|
||||
idaman bool ida_export calc_reference_data(
|
||||
ea_t *target,
|
||||
ea_t *base,
|
||||
ea_t from,
|
||||
const refinfo_t &ri,
|
||||
adiff_t opval);
|
||||
|
||||
|
||||
/// Add xrefs for a reference from the given instruction (\insn_t{ea}).
|
||||
/// This function creates a cross references to the target and the base.
|
||||
/// insn_t::add_off_drefs() calls this function to create xrefs for
|
||||
/// 'offset' operand.
|
||||
/// \param insn the referencing instruction
|
||||
/// \param from the referencing instruction/data address
|
||||
/// \param ri reference info block from the database
|
||||
/// \param opval operand value (usually op_t::value or op_t::addr)
|
||||
/// \param type type of xref
|
||||
/// \param opoff offset of the operand from the start of instruction
|
||||
/// \return the target address of the reference
|
||||
idaman ea_t ida_export add_refinfo_dref(
|
||||
const insn_t &insn,
|
||||
ea_t from,
|
||||
const refinfo_t &ri,
|
||||
adiff_t opval,
|
||||
dref_t type,
|
||||
int opoff);
|
||||
|
||||
|
||||
/// Calculates the target, using the provided refinfo_t
|
||||
|
||||
inline ea_t calc_target(ea_t from, adiff_t opval, const refinfo_t &ri)
|
||||
{
|
||||
ea_t target;
|
||||
if ( !calc_reference_data(&target, NULL, from, ri, opval) )
|
||||
return BADADDR;
|
||||
return target;
|
||||
}
|
||||
|
||||
/// Retrieves refinfo_t structure and calculates the target
|
||||
|
||||
inline ea_t calc_target(ea_t from, ea_t ea, int n, adiff_t opval)
|
||||
{
|
||||
refinfo_t ri;
|
||||
return get_refinfo(&ri, ea, n) ? calc_target(from, opval, ri) : BADADDR;
|
||||
}
|
||||
|
||||
/// Calculate the value of the reference base.
|
||||
|
||||
inline ea_t calc_basevalue(ea_t target, ea_t base)
|
||||
{
|
||||
return base - get_segm_base(getseg(target));
|
||||
}
|
||||
|
||||
|
||||
#endif // _OFFSET_HPP
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PARSEJSON_HPP
|
||||
#define PARSEJSON_HPP
|
||||
|
||||
/*! \file parsejson.hpp
|
||||
|
||||
\brief Tools for parsing JSON-formatted input
|
||||
|
||||
See also lex.hpp/parse.hpp for finer-grained functions & documentation.
|
||||
*/
|
||||
#include <lex.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
enum jtype_t
|
||||
{
|
||||
JT_UNKNOWN = 0,
|
||||
JT_NUM,
|
||||
JT_STR,
|
||||
JT_OBJ,
|
||||
JT_ARR,
|
||||
JT_BOOL,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
struct jobj_t;
|
||||
struct jarr_t;
|
||||
|
||||
#define DECLARE_JVALUE_HELPERS(decl) \
|
||||
decl void ida_export jvalue_t_clear(jvalue_t *); \
|
||||
decl void ida_export jvalue_t_copy(jvalue_t *, const jvalue_t &);
|
||||
|
||||
|
||||
struct jvalue_t;
|
||||
DECLARE_JVALUE_HELPERS(idaman)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct jvalue_t
|
||||
{
|
||||
jvalue_t() : _type(JT_UNKNOWN), _num(0) {}
|
||||
jvalue_t(const jvalue_t &o) : _type(JT_UNKNOWN) { jvalue_t_copy(this, o); }
|
||||
~jvalue_t() { clear(); }
|
||||
|
||||
void clear() { jvalue_t_clear(this); }
|
||||
|
||||
jvalue_t &operator=(const jvalue_t &o) { jvalue_t_copy(this, o); return *this; }
|
||||
|
||||
jtype_t type() const { return _type; }
|
||||
int64 num() const { QASSERT(1277, _type == JT_NUM); return _num; }
|
||||
const char *str() const { QASSERT(1278, _type == JT_STR); return _str->c_str(); }
|
||||
const qstring &qstr() const { QASSERT(1623, _type == JT_STR); return *_str; }
|
||||
const jobj_t &obj() const { QASSERT(1279, _type == JT_OBJ); return *_obj; }
|
||||
const jarr_t &arr() const { QASSERT(1280, _type == JT_ARR); return *_arr; }
|
||||
bool vbool() const { QASSERT(1281, _type == JT_BOOL); return _bool; }
|
||||
jobj_t &obj() { QASSERT(1282, _type == JT_OBJ); return *_obj; }
|
||||
jarr_t &arr() { QASSERT(1283, _type == JT_ARR); return *_arr; }
|
||||
|
||||
//lint -sem(jvalue_t::set_str, custodial(1)) function takes ownership of its argument
|
||||
//lint -sem(jvalue_t::set_obj, custodial(1)) function takes ownership of its argument
|
||||
//lint -sem(jvalue_t::set_arr, custodial(1)) function takes ownership of its argument
|
||||
void set_num(int64 i) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_NUM; _num = i; }
|
||||
void set_str(qstring *s) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_STR; _str = s; }
|
||||
void set_obj(jobj_t *o) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_OBJ; _obj = o; }
|
||||
void set_arr(jarr_t *a) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_ARR; _arr = a; }
|
||||
void set_bool(bool b) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_BOOL; _bool = b; }
|
||||
|
||||
jobj_t *extract_obj() { QASSERT(1624, _type == JT_OBJ); jobj_t *o = _obj; _obj = NULL; _type = JT_UNKNOWN; return o; }
|
||||
jarr_t *extract_arr() { QASSERT(1625, _type == JT_ARR); jarr_t *a = _arr; _arr = NULL; _type = JT_UNKNOWN; return a; }
|
||||
|
||||
private:
|
||||
DECLARE_JVALUE_HELPERS(friend)
|
||||
|
||||
jtype_t _type;
|
||||
|
||||
union
|
||||
{
|
||||
int64 _num;
|
||||
qstring *_str;
|
||||
jobj_t *_obj;
|
||||
jarr_t *_arr;
|
||||
bool _bool;
|
||||
};
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(jvalue_t);
|
||||
typedef qvector<jvalue_t> jvalues_t;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
struct kvp_t
|
||||
{
|
||||
qstring key;
|
||||
jvalue_t value;
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(kvp_t);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct jobj_t : public qvector<kvp_t>
|
||||
{
|
||||
bool has_value(const char *k) const { return get_value(k) != NULL; }
|
||||
jvalue_t *get_value(const char *k, jtype_t t=JT_UNKNOWN)
|
||||
{
|
||||
jvalue_t *v = NULL;
|
||||
for ( size_t i = 0, _n = size(); i < _n; ++i )
|
||||
{
|
||||
if ( at(i).key == k )
|
||||
{
|
||||
if ( t == JT_UNKNOWN || at(i).value.type() == t )
|
||||
v = &at(i).value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
const jvalue_t *get_value(const char *k, jtype_t t=JT_UNKNOWN) const
|
||||
{
|
||||
return ((jobj_t *) this)->get_value(k, t);
|
||||
}
|
||||
|
||||
const jvalue_t *get_value_or_fail(const char *k, jtype_t t=JT_UNKNOWN) const
|
||||
{
|
||||
const jvalue_t *v = get_value(k, t);
|
||||
QASSERT(1289, v != NULL);
|
||||
return v;
|
||||
}
|
||||
|
||||
jvalue_t *get_value_or_new(const char *key)
|
||||
{
|
||||
jvalue_t *v = get_value(key);
|
||||
if ( v == NULL )
|
||||
{
|
||||
kvp_t &kvp = push_back();
|
||||
kvp.key = key;
|
||||
v = &kvp.value;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int64 get_num(const char *k) const { return get_value_or_fail(k)->num(); }
|
||||
bool get_bool(const char *k) const { return get_value_or_fail(k)->vbool(); }
|
||||
const char *get_str(const char *k) const { return get_value_or_fail(k)->str(); }
|
||||
const jobj_t &get_obj(const char *k) const { return get_value_or_fail(k)->obj(); }
|
||||
const jarr_t &get_arr(const char *k) const { return get_value_or_fail(k)->arr(); }
|
||||
|
||||
#define DEFINE_FLAG_GETTER(Type, JType, GetExpr) \
|
||||
bool get(Type *out, const char *k) const \
|
||||
{ \
|
||||
const jvalue_t *v = get_value(k, JType); \
|
||||
bool ok = v != NULL; \
|
||||
if ( ok ) \
|
||||
*out = GetExpr; \
|
||||
return ok; \
|
||||
}
|
||||
#define DEFINE_DFLT_GETTER(Type, JType, GetExpr) \
|
||||
Type get(const char *k, Type dflt) const \
|
||||
{ \
|
||||
const jvalue_t *v = get_value(k, JType); \
|
||||
return v != NULL ? GetExpr : dflt; \
|
||||
}
|
||||
#define DEFINE_SETTER(Type, SetExpr) \
|
||||
void put(const char *key, Type value) \
|
||||
{ \
|
||||
jvalue_t *v = get_value_or_new(key); \
|
||||
SetExpr; \
|
||||
}
|
||||
#define DEFINE_ACCESSORS(Type, ConstType, JType, GetExpr, SetExpr) \
|
||||
DEFINE_FLAG_GETTER(ConstType, JType, GetExpr) \
|
||||
DEFINE_DFLT_GETTER(ConstType, JType, GetExpr) \
|
||||
DEFINE_SETTER(Type, SetExpr)
|
||||
|
||||
DEFINE_ACCESSORS(int, int, JT_NUM, v->num(), v->set_num(value));
|
||||
DEFINE_ACCESSORS(int64, int64, JT_NUM, v->num(), v->set_num(value));
|
||||
DEFINE_ACCESSORS(bool, bool, JT_BOOL, v->vbool(), v->set_bool(value));
|
||||
DEFINE_ACCESSORS(jarr_t *, const jarr_t *, JT_ARR, &v->arr(), v->set_arr(value)); // setter takes ownership
|
||||
DEFINE_ACCESSORS(jobj_t *, const jobj_t *, JT_OBJ, &v->obj(), v->set_obj(value)); // setter takes ownership
|
||||
DEFINE_ACCESSORS(const char *, const char *, JT_STR, v->str(), v->set_str(new qstring(value)));
|
||||
#undef DEFINE_ACCESSORS
|
||||
#undef DEFINE_SETTER
|
||||
#undef DEFINE_DFLT_GETTER
|
||||
#undef DEFINE_FLAG_GETTER
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(jobj_t);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
struct jarr_t
|
||||
{
|
||||
jvalues_t values;
|
||||
|
||||
size_t count_items_with_type(jtype_t t) const
|
||||
{
|
||||
size_t cnt = 0;
|
||||
for ( size_t i = 0, n = values.size(); i < n; ++i )
|
||||
if ( values[i].type() == t )
|
||||
++cnt;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
bool is_homogeneous(jtype_t t) const
|
||||
{
|
||||
return count_items_with_type(t) == values.size();
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(jarr_t);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Note: If 'ungot_tokens' is not NULL, its contents will be used before fetching tokens from the lexer
|
||||
idaman THREAD_SAFE error_t ida_export parse_json(jvalue_t *out, lexer_t *lx, tokenstack_t *ungot_tokens = NULL);
|
||||
idaman THREAD_SAFE error_t ida_export parse_json_string(jvalue_t *out, const char *s);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#define SJF_PRETTY 0x1
|
||||
idaman THREAD_SAFE bool ida_export serialize_json(
|
||||
qstring *out,
|
||||
const jvalue_t &v,
|
||||
uint32 flags=0);
|
||||
|
||||
inline THREAD_SAFE bool serialize_json(
|
||||
qstring *out,
|
||||
const jobj_t *o,
|
||||
uint32 flags=0)
|
||||
{
|
||||
jvalue_t v;
|
||||
v.set_obj((jobj_t *) o);
|
||||
bool rc = serialize_json(out, v, flags);
|
||||
v.extract_obj();
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif // PARSEJSON_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PROBLEMS_HPP
|
||||
#define _PROBLEMS_HPP
|
||||
|
||||
/*! \file problems.hpp
|
||||
|
||||
\brief Functions that deal with the list of problems.
|
||||
|
||||
There are several problem lists. An address may be inserted to any list.
|
||||
The kernel simply maintains these lists, no additional processing
|
||||
is done.
|
||||
|
||||
The problem lists are accessible for the user
|
||||
from the View->Subviews->Problems menu item.
|
||||
|
||||
Addresses in the lists are kept sorted. In general IDA just maintains
|
||||
these lists without using them during analysis (except PR_ROLLED).
|
||||
|
||||
*/
|
||||
|
||||
typedef uchar problist_id_t; ///< see \ref PR_
|
||||
|
||||
/// \defgroup PR_ Problem types
|
||||
//@{
|
||||
const problist_id_t
|
||||
PR_NOBASE = 1, ///< Can't find offset base
|
||||
PR_NONAME = 2, ///< Can't find name
|
||||
PR_NOFOP = 3, ///< Can't find forced op (not used anymore)
|
||||
PR_NOCMT = 4, ///< Can't find comment (not used anymore)
|
||||
PR_NOXREFS = 5, ///< Can't find references
|
||||
PR_JUMP = 6, ///< Jump by table !!!! ignored
|
||||
PR_DISASM = 7, ///< Can't disasm
|
||||
PR_HEAD = 8, ///< Already head
|
||||
PR_ILLADDR = 9, ///< Exec flows beyond limits
|
||||
PR_MANYLINES = 10, ///< Too many lines
|
||||
PR_BADSTACK = 11, ///< Failed to trace the value of the stack pointer
|
||||
PR_ATTN = 12, ///< Attention! Probably erroneous situation.
|
||||
PR_FINAL = 13, ///< Decision to convert to instruction/data is made by IDA
|
||||
PR_ROLLED = 14, ///< The decision made by IDA was wrong and rolled back
|
||||
PR_COLLISION = 15, ///< FLAIR collision: the function with the given name already exists
|
||||
PR_DECIMP = 16, ///< FLAIR match indecision: the patterns matched, but not the function(s) being referenced
|
||||
PR_END = 17; ///< Number of problem types
|
||||
//@}
|
||||
|
||||
/// Get the human-friendly description of the problem,
|
||||
/// if one was provided to remember_problem.
|
||||
/// \param buf a buffer to store the message into.
|
||||
/// \param t problem list type.
|
||||
/// \param ea linear address.
|
||||
/// \return the message length or -1 if none
|
||||
|
||||
idaman ssize_t ida_export get_problem_desc(qstring *buf, problist_id_t t, ea_t ea);
|
||||
|
||||
|
||||
/// Insert an address to a list of problems.
|
||||
/// Display a message saying about the problem (except of ::PR_ATTN,::PR_FINAL)
|
||||
/// ::PR_JUMP is temporarily ignored.
|
||||
/// \param type problem list type
|
||||
/// \param ea linear address
|
||||
/// \param msg a user-friendly message to be displayed instead of
|
||||
/// the default more generic one associated with
|
||||
/// the type of problem. Defaults to NULL.
|
||||
|
||||
idaman void ida_export remember_problem(problist_id_t type, ea_t ea, const char *msg = NULL);
|
||||
|
||||
|
||||
/// Get an address from the specified problem list.
|
||||
/// The address is not removed from the list.
|
||||
/// \param type problem list type
|
||||
/// \param lowea the returned address will be higher or equal
|
||||
/// than the specified address
|
||||
/// \return linear address or #BADADDR
|
||||
|
||||
idaman ea_t ida_export get_problem(problist_id_t type, ea_t lowea);
|
||||
|
||||
|
||||
/// Remove an address from a problem list
|
||||
/// \param type problem list type
|
||||
/// \param ea linear address
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export forget_problem(problist_id_t type, ea_t ea);
|
||||
|
||||
|
||||
/// Get problem list description
|
||||
|
||||
idaman const char *ida_export get_problem_name(problist_id_t type, bool longname=true);
|
||||
|
||||
|
||||
/// Check if the specified address is present in the problem list
|
||||
|
||||
idaman bool ida_export is_problem_present(problist_id_t t, ea_t ea);
|
||||
|
||||
|
||||
inline bool was_ida_decision(ea_t ea) { return is_problem_present(PR_FINAL, ea); }
|
||||
|
||||
|
||||
#endif // _PROBLEMS_HPP
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PRODIR_H
|
||||
#define _PRODIR_H
|
||||
|
||||
//-V:qffblk_t:730 not all members of a class are initialized inside the constructor
|
||||
//-V:qffblk64_t:730
|
||||
|
||||
|
||||
/*! \file prodir.h
|
||||
|
||||
\brief Unified interface to qfindfirst(),qfindnext(),qfindclose() functions.
|
||||
|
||||
These are low level functions, it is better to use enumerate_files2().
|
||||
*/
|
||||
|
||||
/// \def{DIRCHAR, Path separator}
|
||||
/// \def{SDIRCHAR, Path separator as a string}
|
||||
/// \def{DRVCHAR, Windows drive separator}
|
||||
#ifdef __NT__
|
||||
#define __FAT__
|
||||
#define SDIRCHAR "\\"
|
||||
#define DIRCHAR '\\'
|
||||
#define DRVCHAR ':'
|
||||
#else
|
||||
#define SDIRCHAR "/"
|
||||
#define DIRCHAR '/'
|
||||
#endif
|
||||
|
||||
/// Extension character is '.' for all systems
|
||||
#define EXTCHAR '.'
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// \struct{qffblk_t, Various file statistics returned by qfind()-like functions}
|
||||
/// \var{qffblk_t::ff_name, file path}
|
||||
/// \var{qffblk_t::ff_fsize, file size}
|
||||
/// \var{qffblk_t::ff_attrib, file attribute}
|
||||
/// \var{qffblk_t::ff_ftime, file time stamp (ms dos fat format)}
|
||||
/// \var{qffblk_t::ff_fdate, file date stamp (ms dos fat format)}
|
||||
/// \def{FA_RDONLY, File cannot be opened for writing}
|
||||
/// \def{FA_DIREC, Directory}
|
||||
/// \def{FA_ARCH, File has not been backed up}
|
||||
/// \def{MAXPATH, Size limit of qffblk_t::ff_name}
|
||||
#if defined(__UNIX__)
|
||||
#define MAXPATH QMAXPATH
|
||||
struct qffblk_t // Unix
|
||||
{
|
||||
// user fields:
|
||||
int ff_attrib;
|
||||
#define FA_DIREC S_IFDIR
|
||||
#define FA_ARCH 0
|
||||
#define FA_RDONLY 0
|
||||
char ff_name[QMAXPATH];
|
||||
uint32 ff_fsize;
|
||||
uint16 ff_fdate;
|
||||
uint16 ff_ftime;
|
||||
// private fields:
|
||||
void *filelist;
|
||||
int fileidx, fileqty;
|
||||
char dirpath[QMAXPATH];
|
||||
char pattern[QMAXPATH];
|
||||
int attr;
|
||||
qffblk_t(void) : filelist(NULL), fileqty(0) {}
|
||||
};
|
||||
#elif !defined(__X86__)
|
||||
// Win64 - use Visual Studio's ffblk
|
||||
#define MAXPATH _MAX_PATH
|
||||
struct qffblk_t
|
||||
{
|
||||
// inlined __finddata64_t from Visual Studio (for compatibility with IDA 7.0 API)
|
||||
unsigned attrib;
|
||||
// Time values are stored in UTC format.
|
||||
__time64_t time_create; // -1 for FAT file systems
|
||||
__time64_t time_access; // -1 for FAT file systems
|
||||
__time64_t time_write;
|
||||
__int64 size;
|
||||
char name[260]; // NB: file name is assumed to be UTF-8
|
||||
intptr_t handle; // handle returned by _wfindfirst64
|
||||
int attr; // attr value passed to qfindfirst. only FA_DIREC is checked
|
||||
#define FA_RDONLY 0x01
|
||||
#define FA_DIREC 0x10
|
||||
#define FA_ARCH 0x20
|
||||
#define ff_name name
|
||||
#define ff_attrib attrib
|
||||
#define ff_fsize size
|
||||
unsigned short ff_ftime; // FAT/DOS format modification time
|
||||
unsigned short ff_fdate; // FAT/DOS format modification date
|
||||
qffblk_t(void) : handle(-1) {}
|
||||
};
|
||||
CASSERT(sizeof(qffblk_t) == 0x140);
|
||||
#else
|
||||
#define MAXPATH 260
|
||||
struct qffblk_t // Win32 - use Borland's ffblk (IDA 6.95 API compatibility)
|
||||
{
|
||||
long ff_reserved;
|
||||
long ff_fsize;
|
||||
unsigned long ff_attrib;
|
||||
#define FA_RDONLY 0x01
|
||||
#define FA_DIREC 0x10
|
||||
#define FA_ARCH 0x20
|
||||
unsigned short ff_ftime;
|
||||
unsigned short ff_fdate;
|
||||
char ff_name[MAXPATH];
|
||||
qffblk_t(void) : ff_reserved(0) {}
|
||||
};
|
||||
#endif
|
||||
|
||||
/// \def{MAXDRIVE, Max drive name size}
|
||||
/// \def{MAXDIR, Max directory name size}
|
||||
/// \def{MAXFILE, Max file name size}
|
||||
/// \def{MAXEXT, Max file extension size}
|
||||
#if defined(__UNIX__)
|
||||
#define MAXDRIVE QMAXPATH
|
||||
#define MAXDIR QMAXPATH
|
||||
#define MAXFILE QMAXPATH
|
||||
#define MAXEXT QMAXPATH
|
||||
#else
|
||||
#define MAXDRIVE _MAX_DRIVE
|
||||
#define MAXDIR _MAX_DIR
|
||||
#define MAXFILE _MAX_FNAME
|
||||
#define MAXEXT _MAX_EXT
|
||||
#endif
|
||||
|
||||
/// Find first file that matches the pattern.
|
||||
/// \param pattern file name pattern, usually with * and ? wildcards
|
||||
/// \param blk structure that will hold the answer.
|
||||
/// blk->ff_name will hold the file name, for example.
|
||||
/// \param attr the desired file types (#FA_DIREC for directories only or 0 for both directories and files)
|
||||
/// \return 0 if found a file, other values mean error (check qerrno)
|
||||
|
||||
idaman THREAD_SAFE int ida_export qfindfirst(
|
||||
const char *pattern,
|
||||
struct qffblk64_t *blk,
|
||||
int attr);
|
||||
|
||||
|
||||
/// Find next file that matches the pattern.
|
||||
/// \param blk structure that holds the current state.
|
||||
/// blk->ff_name will hold the next file name upon return.
|
||||
/// \return 0 if found the next file, other values mean error (check qerrno)
|
||||
|
||||
idaman THREAD_SAFE int ida_export qfindnext(struct qffblk64_t *blk);
|
||||
|
||||
/// Stop the file enumeration and free internal structures.
|
||||
/// \note usually there is no need to call this function manually, it is called
|
||||
/// from the ::qffblk64_t destructor.
|
||||
/// \param blk file enumeration structure
|
||||
|
||||
idaman THREAD_SAFE void ida_export qfindclose(struct qffblk64_t *blk);
|
||||
|
||||
|
||||
/// Common structure with 64-bit ff_fsize - see ::qffblk_t.
|
||||
struct qffblk64_t
|
||||
{
|
||||
int ff_attrib;
|
||||
char ff_name[QMAXPATH];
|
||||
uint64 ff_fsize;
|
||||
uint16 ff_fdate;
|
||||
uint16 ff_ftime;
|
||||
// private field
|
||||
struct qffblk_t base;
|
||||
|
||||
qffblk64_t(void) {}
|
||||
~qffblk64_t(void) { qfindclose(this); }
|
||||
};
|
||||
|
||||
#endif // _PRODIR_H
|
||||
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _RANGE_HPP
|
||||
#define _RANGE_HPP
|
||||
|
||||
/*! \file range.hpp
|
||||
|
||||
\brief Contains the definition of ::rangecb_t.
|
||||
|
||||
This is a base class used by many parts of IDA.
|
||||
It is a collection of address ranges in the program.
|
||||
It conceptually consists of separate ::range_t instances.
|
||||
|
||||
A range is a non-empty continuous range of addresses (specified by
|
||||
its start and end addresses, the end address is excluded from the
|
||||
range) with some characteristics. For example, the ensemble of program
|
||||
segments is represented by an "rangecb_t" called "segs".
|
||||
|
||||
Ranges are stored in the Btree part of the IDA database.
|
||||
To learn more about Btrees (Balanced Trees):
|
||||
http://www.bluerwhite.org/btree/
|
||||
*/
|
||||
|
||||
#ifndef SWIG
|
||||
struct range_t;
|
||||
/// Helper function. Should not be called directly!
|
||||
idaman size_t ida_export range_t_print(const range_t *, char *buf, size_t bufsize);
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Base class for an range. This class is used as a base class for
|
||||
/// a class with real information - see segment.hpp for example.
|
||||
/// The end address points beyond the range.
|
||||
struct range_t
|
||||
{
|
||||
friend size_t ida_export range_t_print(const range_t *cb, char *buf, size_t bufsize);
|
||||
ea_t start_ea; ///< start_ea included
|
||||
ea_t end_ea; ///< end_ea excluded
|
||||
/// Constructor
|
||||
range_t(void) : start_ea(0), end_ea(0) {}
|
||||
/// Constructor
|
||||
range_t(ea_t ea1, ea_t ea2) : start_ea(ea1), end_ea(ea2) {}
|
||||
|
||||
/// Compare two range_t instances, based on the start_ea
|
||||
int compare(const range_t &r) const { return start_ea > r.start_ea ? 1 : start_ea < r.start_ea ? -1 : 0; }
|
||||
|
||||
bool operator ==(const range_t &r) const { return compare(r) == 0; } ///< Compare two range_t's with '=='
|
||||
bool operator !=(const range_t &r) const { return compare(r) != 0; } ///< Compare two range_t's with '!='
|
||||
bool operator > (const range_t &r) const { return compare(r) > 0; } ///< Compare two range_t's with '<'
|
||||
bool operator < (const range_t &r) const { return compare(r) < 0; } ///< Compare two range_t's with '>'
|
||||
|
||||
/// Is 'ea' in the address range?
|
||||
bool contains(ea_t ea) const { return start_ea <= ea && end_ea > ea; }
|
||||
|
||||
/// Is every ea in 'r' also in this range_t?
|
||||
bool contains(const range_t &r) const { return r.start_ea >= start_ea && r.end_ea <= end_ea; }
|
||||
|
||||
/// Is there an ea in 'r' that is also in this range_t?
|
||||
bool overlaps(const range_t &r) const { return r.start_ea < end_ea && start_ea < r.end_ea; }
|
||||
|
||||
/// Set #start_ea, #end_ea to 0
|
||||
void clear(void) { start_ea = end_ea = 0; }
|
||||
|
||||
/// Is the size of the range_t <= 0?
|
||||
bool empty(void) const { return start_ea >= end_ea; }
|
||||
|
||||
/// Get #end_ea - #start_ea
|
||||
asize_t size(void) const { return end_ea - start_ea; }
|
||||
|
||||
/// Assign the range_t to the intersection between the range_t and 'r'
|
||||
void intersect(const range_t &r)
|
||||
{
|
||||
if ( start_ea < r.start_ea )
|
||||
start_ea = r.start_ea;
|
||||
if ( end_ea > r.end_ea )
|
||||
end_ea = r.end_ea;
|
||||
if ( end_ea < start_ea )
|
||||
end_ea = start_ea;
|
||||
}
|
||||
|
||||
/// Ensure that the range_t includes 'ea'
|
||||
void extend(ea_t ea)
|
||||
{
|
||||
if ( start_ea > ea )
|
||||
start_ea = ea;
|
||||
if ( end_ea < ea )
|
||||
end_ea = ea;
|
||||
}
|
||||
|
||||
/// Print the range_t.
|
||||
/// \param buf the output buffer
|
||||
/// \param bufsize the size of the buffer
|
||||
size_t print(char *buf, size_t bufsize) const { return range_t_print(this, buf, bufsize); }
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(range_t);
|
||||
typedef qvector<range_t> rangevec_base_t;
|
||||
struct rangevec_t : public rangevec_base_t /// Vector of range_t instances
|
||||
{
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Various kinds of ranges, see
|
||||
// \ref idb_event::changing_range_cmt
|
||||
// \ref idb_event::range_cmt_changed
|
||||
enum range_kind_t
|
||||
{
|
||||
RANGE_KIND_UNKNOWN,
|
||||
RANGE_KIND_FUNC, ///< \ref func_t
|
||||
RANGE_KIND_SEGMENT, ///< \ref segment_t
|
||||
RANGE_KIND_HIDDEN_RANGE, ///< \ref hidden_range_t
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Helper functions. Should not be called directly!
|
||||
#ifndef SWIG
|
||||
#define RANGESET_HELPER_DEFINITIONS(decl) \
|
||||
decl bool ida_export rangeset_t_add(rangeset_t *, const range_t &range);\
|
||||
decl bool ida_export rangeset_t_sub(rangeset_t *, const range_t &range);\
|
||||
decl bool ida_export rangeset_t_add2(rangeset_t *, const rangeset_t &aset);\
|
||||
decl bool ida_export rangeset_t_sub2(rangeset_t *, const rangeset_t &aset);\
|
||||
decl bool ida_export rangeset_t_has_common(const rangeset_t *, const range_t &range, bool strict);\
|
||||
decl bool ida_export rangeset_t_has_common2(const rangeset_t *, const rangeset_t &aset);\
|
||||
decl bool ida_export rangeset_t_contains(const rangeset_t *, const rangeset_t &aset);\
|
||||
decl size_t ida_export rangeset_t_print(const rangeset_t *, char *buf, size_t bufsize);\
|
||||
decl bool ida_export rangeset_t_intersect(rangeset_t *, const rangeset_t &aset);\
|
||||
decl const range_t *ida_export rangeset_t_find_range(const rangeset_t *, ea_t ea);\
|
||||
decl ea_t ida_export rangeset_t_next_addr(const rangeset_t *, ea_t ea);\
|
||||
decl ea_t ida_export rangeset_t_prev_addr(const rangeset_t *, ea_t ea);\
|
||||
decl ea_t ida_export rangeset_t_next_range(const rangeset_t *, ea_t ea);\
|
||||
decl ea_t ida_export rangeset_t_prev_range(const rangeset_t *, ea_t ea);\
|
||||
decl rangevec_t::const_iterator ida_export rangeset_t_lower_bound(const rangeset_t *, ea_t ea);\
|
||||
decl rangevec_t::const_iterator ida_export rangeset_t_upper_bound(const rangeset_t *, ea_t ea);\
|
||||
decl void ida_export rangeset_t_swap(rangeset_t *, rangeset_t &r);
|
||||
#else
|
||||
#define RANGESET_HELPER_DEFINITIONS(decl)
|
||||
#endif // SWIG
|
||||
|
||||
class rangeset_t;
|
||||
|
||||
RANGESET_HELPER_DEFINITIONS(idaman)
|
||||
|
||||
/// An ordered set of non-overlapping address ranges
|
||||
class rangeset_t
|
||||
{
|
||||
rangevec_t bag;
|
||||
mutable const range_t *cache;
|
||||
int undo_code = -1;
|
||||
|
||||
RANGESET_HELPER_DEFINITIONS(friend)
|
||||
bool verify(void) const;
|
||||
public:
|
||||
DEFINE_MEMORY_ALLOCATION_FUNCS()
|
||||
/// Constructor
|
||||
rangeset_t(void) : cache(NULL) {}
|
||||
/// Constructor - Initialize set with 'range'
|
||||
rangeset_t(const range_t &range): cache(NULL) { if ( !range.empty() ) bag.push_back(range); }
|
||||
/// Constructor - Initialize set with 'ivs'
|
||||
rangeset_t(const rangeset_t &ivs) : bag(ivs.bag), cache(NULL) {}
|
||||
rangeset_t &operator=(const rangeset_t &ivs) { bag = ivs.bag; cache = NULL; return *this; }
|
||||
/// Set this = 'r' and 'r' = this. See qvector::swap()
|
||||
void swap(rangeset_t &r) { rangeset_t_swap(this, r); }
|
||||
|
||||
/// Add an address range to the set.
|
||||
/// If 'range' intersects an existing element e, then e is extended
|
||||
/// to include 'range', and any superfluous elements (subsets of e) are removed.
|
||||
/// \param range address range to add. cannot be empty
|
||||
/// \return false if 'range' was not added (the set was unchanged)
|
||||
bool add(const range_t &range) { return rangeset_t_add(this, range); }
|
||||
|
||||
/// Create a new range_t from 'start' and 'end' and add it to the set
|
||||
bool add(ea_t start, ea_t _end) { return add(range_t(start, _end)); }
|
||||
|
||||
/// Add each element of 'aset' to the set.
|
||||
/// \return false if no elements were added (the set was unchanged)
|
||||
bool add(const rangeset_t &aset) { return rangeset_t_add2(this, aset); }
|
||||
|
||||
/// Subtract an address range from the set.
|
||||
/// All subsets of 'range' will be removed, and all elements that intersect
|
||||
/// 'range' will be truncated/split so they do not include 'range'.
|
||||
/// \param range address range to subtract. cannot be empty.
|
||||
/// \return false if 'range' was not subtracted (the set was unchanged)
|
||||
bool sub(const range_t &range) { return rangeset_t_sub(this, range); }
|
||||
|
||||
/// Subtract an ea (an range of size 1) from the set. See sub(const range_t &)
|
||||
bool sub(ea_t ea) { return sub(range_t(ea, ea+1)); }
|
||||
|
||||
/// Subtract each range in 'aset' from the set
|
||||
/// \return false if nothing was subtracted (the set was unchanged)
|
||||
bool sub(const rangeset_t &aset) { return rangeset_t_sub2(this, aset); }
|
||||
|
||||
/// Is there an ea in 'range' that is also in the rangeset?
|
||||
bool has_common(const range_t &range) const
|
||||
{ return rangeset_t_has_common(this, range, false); }
|
||||
|
||||
/// Is every ea in 'range' contained in the rangeset?
|
||||
bool includes(const range_t &range) const
|
||||
{ return rangeset_t_has_common(this, range, true); }
|
||||
|
||||
/// Print each range_t in the rangeset
|
||||
size_t print(char *buf, size_t bufsize) const
|
||||
{ return rangeset_t_print(this, buf, bufsize); }
|
||||
|
||||
/// Size in bytes
|
||||
asize_t count(void) const;
|
||||
|
||||
/// Get the range_t at index 'idx'
|
||||
const range_t &getrange(int idx) const { return bag[idx]; }
|
||||
|
||||
/// Get the last range_t in the set
|
||||
const range_t &lastrange(void) const { return bag.back(); }
|
||||
|
||||
/// Get the number of range_t elements in the set
|
||||
size_t nranges(void) const { return bag.size(); }
|
||||
|
||||
/// Does the set have zero elements
|
||||
bool empty(void) const { return bag.empty(); }
|
||||
|
||||
/// Delete all elements from the set. See qvector::clear()
|
||||
void clear(void) { bag.clear(); cache = NULL; }
|
||||
|
||||
/// Does any element of 'aset' overlap with an element in this rangeset?. See range_t::overlaps()
|
||||
bool has_common(const rangeset_t &aset) const
|
||||
{ return rangeset_t_has_common2(this, aset); }
|
||||
|
||||
/// Does an element of the rangeset contain 'ea'? See range_t::contains(ea_t)
|
||||
bool contains(ea_t ea) const { return !empty() && find_range(ea) != NULL; }
|
||||
|
||||
/// Is every element in 'aset' contained in an element of this rangeset?. See range_t::contains(range_t)
|
||||
bool contains(const rangeset_t &aset) const
|
||||
{ return rangeset_t_contains(this, aset); }
|
||||
|
||||
/// Set the rangeset to its intersection with 'aset'.
|
||||
/// \return false if the set was unchanged
|
||||
bool intersect(const rangeset_t &aset)
|
||||
{ return rangeset_t_intersect(this, aset); }
|
||||
|
||||
/// Is every element in the rangeset contained in an element of 'aset'?
|
||||
bool is_subset_of(const rangeset_t &aset) const { return aset.contains(*this); }
|
||||
|
||||
/// Do this rangeset and 'aset' have identical elements?
|
||||
bool is_equal(const rangeset_t &aset) const { return bag == aset.bag; }
|
||||
|
||||
bool operator==(const rangeset_t &aset) const { return is_equal(aset); } ///< Compare two rangesets with '=='
|
||||
bool operator!=(const rangeset_t &aset) const { return !is_equal(aset); } ///< Compare two rangesets with '!='
|
||||
|
||||
typedef rangevec_t::iterator iterator; ///< Iterator for rangesets
|
||||
typedef rangevec_t::const_iterator const_iterator; ///< Const iterator for rangesets
|
||||
const_iterator begin(void) const { return bag.begin(); } ///< Get an iterator that points to the first element in the set
|
||||
const_iterator end(void) const { return bag.end(); } ///< Get an iterator that points to the end of the set. (This is NOT the last element)
|
||||
iterator begin(void) { return bag.begin(); } ///< \copydoc begin
|
||||
iterator end(void) { return bag.end(); } ///< \copydoc end
|
||||
|
||||
/// Get the first range that contains at least one ea_t value greater than 'ea'
|
||||
const_iterator lower_bound(ea_t ea) const { return rangeset_t_lower_bound(this, ea); }
|
||||
|
||||
/// Get the first range such that every ea_t value in this range is strictly greater than 'ea'
|
||||
const_iterator upper_bound(ea_t ea) const { return rangeset_t_upper_bound(this, ea); }
|
||||
|
||||
/// Get the element from the set that contains 'ea'.
|
||||
/// \return NULL if there is no such element
|
||||
const range_t *find_range(ea_t ea) const
|
||||
{ return rangeset_t_find_range(this, ea); }
|
||||
|
||||
/// When searching the rangeset, we keep a cached element to help speed up searches.
|
||||
/// \return a pointer to the cached element
|
||||
const range_t *cached_range(void) const { return cache; }
|
||||
|
||||
/// Get the smallest ea_t value greater than 'ea' contained in the rangeset
|
||||
ea_t next_addr(ea_t ea) const { return rangeset_t_next_addr(this, ea); }
|
||||
|
||||
/// Get the largest ea_t value less than 'ea' contained in the rangeset
|
||||
ea_t prev_addr(ea_t ea) const { return rangeset_t_prev_addr(this, ea); }
|
||||
|
||||
/// Get the smallest ea_t value greater than 'ea' that is not in the same range as 'ea'
|
||||
ea_t next_range(ea_t ea) const { return rangeset_t_next_range(this, ea); }
|
||||
|
||||
/// Get the largest ea_t value less than 'ea' that is not in the same range as 'ea'
|
||||
ea_t prev_range(ea_t ea) const { return rangeset_t_prev_range(this, ea); }
|
||||
|
||||
/// Subtract the address range (from, from+size) and add the range (to, to+size)
|
||||
int move_chunk(ea_t from, ea_t to, asize_t size);
|
||||
|
||||
/// TODO: return code borrowed from va.hpp, same with move_chunk()
|
||||
int check_move_args(ea_t from, ea_t to, asize_t size);
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(rangeset_t);
|
||||
typedef qvector<rangeset_t> array_of_rangesets; ///< Array of rangeset_t objects
|
||||
typedef qvector<const rangeset_t*> rangeset_crefvec_t;
|
||||
|
||||
#endif // _RANGE_HPP
|
||||
@@ -1,240 +0,0 @@
|
||||
/*************************************************
|
||||
* Perl-Compatible Regular Expressions *
|
||||
*************************************************/
|
||||
|
||||
/* PCRE2 is a library of functions to support regular expressions whose syntax
|
||||
and semantics are as close as possible to those of the Perl 5 language.
|
||||
|
||||
Written by Philip Hazel
|
||||
Original API code Copyright (c) 1997-2012 University of Cambridge
|
||||
New API code Copyright (c) 2016 University of Cambridge
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Cambridge nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _REGEX_H_
|
||||
#define _REGEX_H_
|
||||
#ifdef __cplusplus
|
||||
#include <map>
|
||||
#include <kernwin.hpp>
|
||||
#endif
|
||||
|
||||
#include <pro.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#undef __P
|
||||
#endif
|
||||
|
||||
typedef off_t regoff_t;
|
||||
/* The structure representing a compiled regular expression. */
|
||||
|
||||
struct regex_t
|
||||
{
|
||||
int re_magic;
|
||||
size_t re_nsub; /* number of parenthesized subexpressions */
|
||||
const char *re_endp; /* end pointer for REG_PEND */
|
||||
void *re_g; /* none of your business :-) */
|
||||
};
|
||||
|
||||
/* The structure in which a captured offset is returned. */
|
||||
|
||||
struct regmatch_t
|
||||
{
|
||||
regoff_t rm_so; /* start of match */
|
||||
regoff_t rm_eo; /* end of match */
|
||||
};
|
||||
#ifndef REG_ICASE
|
||||
/* Options, mostly defined by POSIX, but with some extras. */
|
||||
|
||||
#define REG_ICASE 0x0001 /* Maps to PCRE2_CASELESS */
|
||||
#define REG_NEWLINE 0x0002 /* Maps to PCRE2_MULTILINE */
|
||||
#define REG_NOTBOL 0x0004 /* Maps to PCRE2_NOTBOL */
|
||||
#define REG_NOTEOL 0x0008 /* Maps to PCRE2_NOTEOL */
|
||||
#define REG_DOTALL 0x0010 /* NOT defined by POSIX; maps to PCRE2_DOTALL */
|
||||
#define REG_NOSUB 0x0020 /* Maps to PCRE2_NO_AUTO_CAPTURE */
|
||||
#define REG_UTF 0x0040 /* NOT defined by POSIX; maps to PCRE2_UTF */
|
||||
#define REG_STARTEND 0x0080 /* BSD feature: pass subject string by so,eo */
|
||||
#define REG_NOTEMPTY 0x0100 /* NOT defined by POSIX; maps to PCRE2_NOTEMPTY */
|
||||
#define REG_UNGREEDY 0x0200 /* NOT defined by POSIX; maps to PCRE2_UNGREEDY */
|
||||
#define REG_UCP 0x0400 /* NOT defined by POSIX; maps to PCRE2_UCP */
|
||||
|
||||
/* This is not used by PCRE2, but by defining it we make it easier
|
||||
to slot PCRE2 into existing programs that make POSIX calls. */
|
||||
|
||||
#define REG_EXTENDED 0
|
||||
#define REG_TRACE 0 // unsupported by PCRE2
|
||||
|
||||
/* Error values. Not all these are relevant or used by the wrapper. */
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
REG_ASSERT = 1, /* internal error ? */
|
||||
REG_BADBR, /* invalid repeat counts in {} */
|
||||
REG_BADPAT, /* pattern error */
|
||||
REG_BADRPT, /* ? * + invalid */
|
||||
REG_EBRACE, /* unbalanced {} */
|
||||
REG_EBRACK, /* unbalanced [] */
|
||||
REG_ECOLLATE, /* collation error - not relevant */
|
||||
REG_ECTYPE, /* bad class */
|
||||
REG_EESCAPE, /* bad escape sequence */
|
||||
REG_EMPTY, /* empty expression */
|
||||
REG_EPAREN, /* unbalanced () */
|
||||
REG_ERANGE, /* bad range inside [] */
|
||||
REG_ESIZE, /* expression too big */
|
||||
REG_ESPACE, /* failed to get memory */
|
||||
REG_ESUBREG, /* bad back reference */
|
||||
REG_INVARG, /* bad argument */
|
||||
REG_NOMATCH /* match failed */
|
||||
};
|
||||
#endif //REG_ICASE
|
||||
|
||||
/* The functions */
|
||||
|
||||
// compile the regular expression
|
||||
idaman THREAD_SAFE int ida_export qregcomp(
|
||||
struct regex_t *preg,
|
||||
const char *pattern,
|
||||
int cflags);
|
||||
|
||||
// mapping from error codes returned by qregcomp() and qregexec() to a string
|
||||
idaman THREAD_SAFE size_t ida_export qregerror(
|
||||
int errcode,
|
||||
const struct regex_t *preg,
|
||||
char *errbuf,
|
||||
size_t errbuf_size);
|
||||
|
||||
// match regex against a string
|
||||
idaman THREAD_SAFE int ida_export qregexec(
|
||||
const struct regex_t *preg,
|
||||
const char *str,
|
||||
size_t nmatch,
|
||||
struct regmatch_t pmatch[],
|
||||
int eflags);
|
||||
|
||||
// free any memory allocated by qregcomp
|
||||
idaman THREAD_SAFE void ida_export qregfree(struct regex_t *preg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class refcnted_regex_t : public qrefcnt_obj_t
|
||||
{
|
||||
regex_t regex;
|
||||
|
||||
refcnted_regex_t()
|
||||
{
|
||||
regex = {};
|
||||
}
|
||||
virtual ~refcnted_regex_t()
|
||||
{
|
||||
qregfree(®ex);
|
||||
}
|
||||
public:
|
||||
virtual void idaapi release(void) override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
int exec(const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
|
||||
{
|
||||
return qregexec(®ex, string, nmatch, pmatch, eflags);
|
||||
}
|
||||
int process_errors(int code, qstring *errmsg)
|
||||
{
|
||||
if ( code != 0 && errmsg != NULL )
|
||||
{
|
||||
char errbuf[MAXSTR];
|
||||
qregerror(code, ®ex, errbuf, sizeof(errbuf));
|
||||
*errmsg = errbuf;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
static refcnted_regex_t *create(const qstring &text, bool case_insensitive, qstring *errmsg)
|
||||
{
|
||||
if ( text.empty() )
|
||||
return NULL;
|
||||
refcnted_regex_t *p = new refcnted_regex_t();
|
||||
int rflags = REG_EXTENDED;
|
||||
if ( case_insensitive )
|
||||
rflags |= REG_ICASE;
|
||||
int code = qregcomp(&p->regex, text.begin(), rflags);
|
||||
if ( p->process_errors(code, errmsg) != 0 )
|
||||
{
|
||||
// It is unnecessary to qregfree() here: the deletion of 'p' will
|
||||
// call qregfree (but anyway, even that is unnecessary, because
|
||||
// if we end up here, it means qregcomp() failed, and when that
|
||||
// happens, qregcomp() frees the regex itself.)
|
||||
delete p;
|
||||
p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
size_t nsub(void)
|
||||
{
|
||||
/* number of parenthesized subexpressions */
|
||||
return regex.re_nsub;
|
||||
}
|
||||
DECLARE_UNCOPYABLE(refcnted_regex_t);
|
||||
};
|
||||
typedef qrefcnt_t<refcnted_regex_t> regex_ptr_t;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
struct regex_cache_t
|
||||
{
|
||||
regex_ptr_t &find_or_create(const qstring &str)
|
||||
{
|
||||
regex_cache_map_t::iterator it = cache.find(str);
|
||||
if ( it == cache.end() )
|
||||
{
|
||||
qstring errmsg;
|
||||
regex_ptr_t rx = regex_ptr_t(refcnted_regex_t::create(str, false, &errmsg));
|
||||
if ( rx == NULL )
|
||||
error("%s", errmsg.c_str());
|
||||
it = cache.insert(regex_cache_map_t::value_type(str, rx)).first;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::map<qstring,regex_ptr_t> regex_cache_map_t;
|
||||
regex_cache_map_t cache;
|
||||
};
|
||||
|
||||
#endif //__cplusplus
|
||||
|
||||
#ifndef NO_OBSOLETE_FUNCS
|
||||
idaman DEPRECATED THREAD_SAFE int ida_export regcomp(struct regex_t *preg, const char *pattern, int cflags);
|
||||
idaman DEPRECATED THREAD_SAFE size_t ida_export regerror(int errcode, const struct regex_t *preg, char *errbuf, size_t errbuf_size);
|
||||
idaman DEPRECATED THREAD_SAFE int ida_export regexec(const struct regex_t *preg, const char *str, size_t nmatch, struct regmatch_t pmatch[], int eflags);
|
||||
idaman DEPRECATED THREAD_SAFE void ida_export regfree(struct regex_t *preg);
|
||||
#endif
|
||||
|
||||
#endif /* !_REGEX_H_ */
|
||||
@@ -1,375 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __REGISTRY_HPP
|
||||
#define __REGISTRY_HPP
|
||||
|
||||
/*! \file registry.hpp
|
||||
|
||||
\brief Registry related functions
|
||||
|
||||
IDA uses the registry to store global configuration options that must
|
||||
persist after IDA has been closed.
|
||||
|
||||
On Windows, IDA uses the Windows registry directly. On Unix systems, the registry
|
||||
is stored in a file (typically ~/.idapro/ida.reg).
|
||||
|
||||
The root key for accessing IDA settings in the registry is defined by #ROOT_KEY_NAME.
|
||||
*/
|
||||
|
||||
/// Key used to store IDA settings in registry (Windows version).
|
||||
/// \note this name is automatically prepended to all
|
||||
/// key names passed to functions in this file.
|
||||
#define ROOT_KEY_NAME "Software\\Hex-Rays\\IDA"
|
||||
|
||||
/// \cond
|
||||
// Low level functions. DO NOT USE THEM. See the wrappers below.
|
||||
// 'mode' is:
|
||||
// - 0: data is a raw buffer, and its datalen must be able to hold the entire binary contents
|
||||
// - 1: data is a raw buffer, and its datalen doesn't need to be able to hold the entire binary contents
|
||||
// - 2: data is a ::bytevec_t*, datalen is ignored.
|
||||
idaman bool ida_export reg_bin_op(
|
||||
const char *name,
|
||||
bool save,
|
||||
void *data,
|
||||
size_t datalen,
|
||||
const char *subkey,
|
||||
int mode = 0);
|
||||
idaman bool ida_export reg_str_get(qstring *buf, const char *name, const char *subkey);
|
||||
idaman void ida_export reg_str_set(const char *name, const char *subkey, const char *buf);
|
||||
idaman int ida_export reg_int_op(
|
||||
const char *name,
|
||||
bool save,
|
||||
int value,
|
||||
const char *subkey = NULL);
|
||||
/// \endcond
|
||||
|
||||
/// Types of values stored in the registry
|
||||
enum regval_type_t
|
||||
{
|
||||
reg_unknown = 0, ///< unknown
|
||||
reg_sz = 1, ///< utf8 string
|
||||
reg_binary = 3, ///< binary data
|
||||
reg_dword = 4 ///< 32-bit number
|
||||
};
|
||||
|
||||
|
||||
/// Delete a key from the registry
|
||||
idaman bool ida_export reg_delete_subkey(const char *name);
|
||||
|
||||
/// Delete a subtree from the registry
|
||||
idaman bool ida_export reg_delete_tree(const char *name);
|
||||
|
||||
/// Delete a value from the registry.
|
||||
/// \param name value name
|
||||
/// \param subkey parent key
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export reg_delete(const char *name, const char *subkey = NULL);
|
||||
|
||||
|
||||
/// Is there already a key with the given name?
|
||||
|
||||
idaman bool ida_export reg_subkey_exists(const char *name);
|
||||
|
||||
|
||||
/// Is there already a value with the given name?
|
||||
/// \param name value name
|
||||
/// \param subkey parent key
|
||||
|
||||
idaman bool ida_export reg_exists(const char *name, const char *subkey = NULL);
|
||||
|
||||
|
||||
/// Retrieve the child names of the given key.
|
||||
/// \param out result
|
||||
/// \param name key name
|
||||
/// \param subkeys if true, collect subkey names. if false, collect value names.
|
||||
/// \return false if the given key does not exist
|
||||
|
||||
idaman bool ida_export reg_subkey_children(qstrvec_t *out, const char *name, bool subkeys);
|
||||
|
||||
|
||||
/// Get data type of a given value.
|
||||
/// \param out result
|
||||
/// \param name value name
|
||||
/// \param subkey key name
|
||||
/// \return false if the [key+]value doesn't exist
|
||||
|
||||
idaman bool ida_export reg_data_type(regval_type_t *out, const char *name, const char *subkey = NULL);
|
||||
|
||||
|
||||
/// Retrieve all string values associated with the given key.
|
||||
/// Also see reg_update_strlist().
|
||||
|
||||
idaman void ida_export reg_read_strlist(qstrvec_t *list, const char *subkey);
|
||||
|
||||
|
||||
/// Update list of strings associated with given key.
|
||||
/// \param subkey key name
|
||||
/// \param add string to be added to list, can be NULL
|
||||
/// \param maxrecs limit list to this size
|
||||
/// \param rem string to be removed from list, can be NULL
|
||||
/// \param ignorecase ignore case for 'add' and 'rem'
|
||||
|
||||
idaman void ida_export reg_update_strlist(
|
||||
const char *subkey,
|
||||
const char *add,
|
||||
size_t maxrecs,
|
||||
const char *rem = NULL,
|
||||
bool ignorecase = false);
|
||||
|
||||
|
||||
/// Write binary data to the registry.
|
||||
/// \param name value name
|
||||
/// \param data input, must not be NULL
|
||||
/// \param datalen length of input in bytes
|
||||
/// \param subkey key name
|
||||
|
||||
inline void reg_write_binary(
|
||||
const char *name,
|
||||
const void *data,
|
||||
size_t datalen,
|
||||
const char *subkey = NULL)
|
||||
{
|
||||
reg_bin_op(name, true, CONST_CAST(void *)(data), datalen, subkey);
|
||||
}
|
||||
|
||||
|
||||
/// Read binary data from the registry.
|
||||
/// \param name value name
|
||||
/// \param[out] data result, must not be NULL
|
||||
/// \param datalen length of out buffer in bytes
|
||||
/// \param subkey key name
|
||||
/// \return false if 'data' is not large enough to hold all data present.
|
||||
/// in this case 'data' is left untouched.
|
||||
|
||||
inline bool reg_read_binary(
|
||||
const char *name,
|
||||
void *data,
|
||||
size_t datalen,
|
||||
const char *subkey = NULL)
|
||||
{
|
||||
return reg_bin_op(name, false, data, datalen, subkey);
|
||||
}
|
||||
|
||||
|
||||
/// Read a chunk of binary data from the registry.
|
||||
/// This function succeeds even in the case of a partial read.
|
||||
/// \param name value name
|
||||
/// \param[out] data result, must not be NULL
|
||||
/// \param datalen length of output buffer in bytes
|
||||
/// \param subkey key name
|
||||
/// \return success
|
||||
|
||||
inline bool reg_read_binary_part(
|
||||
const char *name,
|
||||
void *data,
|
||||
size_t datalen,
|
||||
const char *subkey = NULL)
|
||||
{
|
||||
return reg_bin_op(name, false, data, datalen, subkey, 1);
|
||||
}
|
||||
|
||||
|
||||
/// Read binary data from the registry.
|
||||
/// \param name value name
|
||||
/// \param[out] data output buffer, must not be NULL
|
||||
/// \param subkey key name
|
||||
/// \return success
|
||||
|
||||
inline bool reg_read_binary(
|
||||
const char *name,
|
||||
bytevec_t *data,
|
||||
const char *subkey = NULL)
|
||||
{
|
||||
return reg_bin_op(name, false, data, 0, subkey, 2);
|
||||
}
|
||||
|
||||
|
||||
/// Write a string to the registry.
|
||||
/// \param name value name
|
||||
/// \param utf8 utf8-encoded string
|
||||
/// \param subkey key name
|
||||
|
||||
inline void reg_write_string(
|
||||
const char *name,
|
||||
const char *utf8,
|
||||
const char *subkey = NULL)
|
||||
{
|
||||
reg_str_set(name, subkey, utf8);
|
||||
}
|
||||
|
||||
|
||||
/// Read a string from the registry.
|
||||
/// \param[out] utf8 output buffer
|
||||
/// \param name value name
|
||||
/// \param subkey key name
|
||||
/// \return success
|
||||
|
||||
inline bool reg_read_string(
|
||||
qstring *utf8,
|
||||
const char *name,
|
||||
const char *subkey = NULL)
|
||||
{
|
||||
return reg_str_get(utf8, name, subkey);
|
||||
}
|
||||
|
||||
|
||||
/// Read integer value from the registry.
|
||||
/// \param name value name
|
||||
/// \param defval default value
|
||||
/// \param subkey key name
|
||||
/// \return the value read from the registry, or 'defval' if the read failed
|
||||
|
||||
inline int reg_read_int(const char *name, int defval, const char *subkey = NULL)
|
||||
{
|
||||
return reg_int_op(name, false, defval, subkey);
|
||||
}
|
||||
|
||||
|
||||
/// Write integer value to the registry.
|
||||
/// \param name value name
|
||||
/// \param value value to write
|
||||
/// \param subkey key name
|
||||
|
||||
inline void reg_write_int(const char *name, int value, const char *subkey = NULL)
|
||||
{
|
||||
reg_int_op(name, true, value, subkey);
|
||||
}
|
||||
|
||||
|
||||
/// Read boolean value from the registry.
|
||||
/// \param name value name
|
||||
/// \param defval default value
|
||||
/// \param subkey key name
|
||||
/// \return boolean read from registry, or 'defval' if the read failed
|
||||
|
||||
inline bool reg_read_bool(const char *name, bool defval, const char *subkey = NULL)
|
||||
{
|
||||
return reg_int_op(name, false, int(defval), subkey) != 0;
|
||||
}
|
||||
|
||||
|
||||
/// Write boolean value to the registry.
|
||||
/// \param name value name
|
||||
/// \param value boolean to write (nonzero = true)
|
||||
/// \param subkey key name
|
||||
|
||||
inline void reg_write_bool(const char *name, int value, const char *subkey = NULL)
|
||||
{
|
||||
reg_int_op(name, true, value != 0, subkey);
|
||||
}
|
||||
|
||||
|
||||
/// Get all subkey names of given key
|
||||
|
||||
inline bool reg_subkey_subkeys(qstrvec_t *out, const char *name)
|
||||
{
|
||||
return reg_subkey_children(out, name, true);
|
||||
}
|
||||
|
||||
|
||||
/// Get all value names under given key
|
||||
|
||||
inline bool reg_subkey_values(qstrvec_t *out, const char *name)
|
||||
{
|
||||
return reg_subkey_children(out, name, false);
|
||||
}
|
||||
|
||||
|
||||
/// Update registry with a file list.
|
||||
/// Case sensitivity will vary depending on the target OS.
|
||||
/// \note 'add' and 'rem' must be UTF-8, just like for regular string operations.
|
||||
|
||||
inline void reg_update_filestrlist(
|
||||
const char *subkey,
|
||||
const char *add,
|
||||
size_t maxrecs,
|
||||
const char *rem = NULL)
|
||||
{
|
||||
reg_update_strlist(
|
||||
subkey, add, maxrecs, rem,
|
||||
#ifdef __NT__ // Ignore case in Windows
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// INTERNALS
|
||||
|
||||
/// \cond
|
||||
#define _RVN_(f) regname_ ## f
|
||||
|
||||
#ifndef __DEFINE_REG_NAMES__
|
||||
#define REG_VAL_NAME(n,s) \
|
||||
extern const char _RVN_(n)[]
|
||||
#else
|
||||
#define REG_VAL_NAME(n,s) \
|
||||
extern const char _RVN_(n)[]; \
|
||||
const char _RVN_(n)[] = s
|
||||
#endif
|
||||
|
||||
#define REG_BOOL_FUNC(func, valname) \
|
||||
REG_VAL_NAME(func, valname); \
|
||||
inline void regset_ ## func(bool value) \
|
||||
{ reg_write_bool(_RVN_(func), value); } \
|
||||
inline bool regget_ ## func(bool def) \
|
||||
{ return reg_read_bool(_RVN_(func), def); }
|
||||
|
||||
#define REG_INT_FUNC(func, valname) \
|
||||
REG_VAL_NAME(func,valname); \
|
||||
inline void regset_ ## func(int value) \
|
||||
{ \
|
||||
reg_int_op(_RVN_(func), true, value); \
|
||||
} \
|
||||
inline int regget_ ## func(int def=0) \
|
||||
{ \
|
||||
return reg_int_op(_RVN_(func), false, def); \
|
||||
}
|
||||
|
||||
idaman void ida_export reg_load(void);
|
||||
idaman void ida_export reg_flush(void);
|
||||
|
||||
// if using history functions below, you have to define the following two variables
|
||||
extern const char regkey_history[];
|
||||
extern int max_history_files; // max number of files in the file menu
|
||||
// and in the welcome box
|
||||
#define MAX_HISTORY_FILES_DEF 10 // default value
|
||||
|
||||
inline void regget_history(qstrvec_t *list)
|
||||
{
|
||||
#ifdef DEMO
|
||||
qnotused(list);
|
||||
#else
|
||||
reg_read_strlist(list, regkey_history);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void reg_update_history(const char *addfile, const char *removefile = NULL)
|
||||
{
|
||||
#ifdef DEMO
|
||||
qnotused(addfile);
|
||||
qnotused(removefile);
|
||||
#else
|
||||
// On Windows avoid duplicate upper/lower-case entries
|
||||
// by using reg_update_filestrlist() which takes care of case sensitivity
|
||||
reg_update_filestrlist(regkey_history, addfile, max_history_files, removefile);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void reg_history_size_truncate(void)
|
||||
{
|
||||
#ifndef DEMO
|
||||
reg_update_strlist(regkey_history, NULL, max_history_files, NULL);
|
||||
#endif
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
#endif // __REGISTRY_HPP
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* ALL RIGHTS RESERVED.
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SEARCH_HPP
|
||||
#define __SEARCH_HPP
|
||||
|
||||
/*! \file search.hpp
|
||||
|
||||
\brief Middle-level search functions
|
||||
|
||||
They all are controlled by \ref SEARCH_
|
||||
*/
|
||||
|
||||
/// \defgroup SEARCH_ Search flags
|
||||
//@{
|
||||
#define SEARCH_UP 0x000 ///< search towards lower addresses
|
||||
#define SEARCH_DOWN 0x001 ///< search towards higher addresses
|
||||
#define SEARCH_NEXT 0x002 ///< skip the starting address when searching.
|
||||
///< this bit is useful only for search(), bin_search2(), find_reg_access().
|
||||
///< find_.. functions skip the starting address automatically.
|
||||
#define SEARCH_CASE 0x004 ///< case-sensitive search (case-insensitive otherwise)
|
||||
#define SEARCH_REGEX 0x008 ///< regular expressions in search string (supported only for the text search)
|
||||
#define SEARCH_NOBRK 0x010 ///< do not test if the user clicked cancel to interrupt the search
|
||||
#define SEARCH_NOSHOW 0x020 ///< do not display the search progress/refresh screen
|
||||
#define SEARCH_IDENT 0x080 ///< search for an identifier (text search).
|
||||
///< it means that the characters before
|
||||
///< and after the match cannot be is_visible_char().
|
||||
#define SEARCH_BRK 0x100 ///< return #BADADDR if the search was cancelled.
|
||||
#define SEARCH_USE 0x200 ///< find_reg_access: search for a use (read access)
|
||||
#define SEARCH_DEF 0x400 ///< find_reg_access: search for a definition (write access)
|
||||
//@}
|
||||
|
||||
|
||||
/// Is the #SEARCH_DOWN bit set?
|
||||
|
||||
inline THREAD_SAFE bool search_down(int sflag) { return (sflag & SEARCH_DOWN) != 0; }
|
||||
|
||||
|
||||
/// \name find_... functions
|
||||
/// \param ea start ea
|
||||
/// \param sflag combination of \ref SEARCH_
|
||||
/// \param[out] opnum filled with operand number whenever relevant
|
||||
/// \return first ea at which the search criteria is met
|
||||
//@{
|
||||
|
||||
|
||||
/// Find next error or problem
|
||||
|
||||
idaman ea_t ida_export find_error(ea_t ea, int sflag, int *opnum=NULL);
|
||||
|
||||
|
||||
/// Find next operand without any type info
|
||||
|
||||
idaman ea_t ida_export find_notype(ea_t ea, int sflag, int *opnum=NULL);
|
||||
|
||||
|
||||
/// Find next unexplored address
|
||||
|
||||
idaman ea_t ida_export find_unknown(ea_t ea, int sflag);
|
||||
|
||||
|
||||
/// Find next ea that is the start of an instruction or data
|
||||
|
||||
idaman ea_t ida_export find_defined(ea_t ea, int sflag);
|
||||
|
||||
|
||||
/// Find next suspicious operand
|
||||
|
||||
idaman ea_t ida_export find_suspop(ea_t ea, int sflag, int *opnum=NULL);
|
||||
|
||||
|
||||
/// Find next data address
|
||||
|
||||
idaman ea_t ida_export find_data(ea_t ea,int sflag);
|
||||
|
||||
|
||||
/// Find next code address
|
||||
|
||||
idaman ea_t ida_export find_code(ea_t ea,int sflag);
|
||||
|
||||
|
||||
/// Find next code address that does not belong to a function
|
||||
|
||||
idaman ea_t ida_export find_not_func(ea_t ea,int sflag);
|
||||
|
||||
|
||||
/// Find next immediate operand with the given value
|
||||
|
||||
idaman ea_t ida_export find_imm(ea_t ea, int sflag, uval_t search_value, int *opnum=NULL);
|
||||
|
||||
|
||||
/// See search()
|
||||
|
||||
idaman ea_t ida_export find_text(ea_t start_ea, int y, int x, const char *ustr, int sflag);
|
||||
|
||||
|
||||
/// Find access to a register.
|
||||
/// \param out pointer to the output buffer. must be non-null.
|
||||
/// upon success contains info about the found register.
|
||||
/// upon failed search for a read access out->range contains
|
||||
/// the info about the non-redefined parts of the register.
|
||||
/// \param start_ea starting address
|
||||
/// \param end_ea ending address. BADADDR means that the end limit is missing.
|
||||
/// otherwise, if the search direction is SEARCH_UP,
|
||||
/// END_EA must be lower than START_EA.
|
||||
/// \param regname the register to search for.
|
||||
/// \param sflag combination of \ref SEARCH_ bits.
|
||||
/// \note This function does not care about the control flow and
|
||||
/// probes all instructions in the specified range, starting from START_EA.
|
||||
/// Only direct references to registers are detected. Function calls and
|
||||
/// system traps are ignored.
|
||||
/// \return the found address. BADADDR if not found or error.
|
||||
idaman ea_t ida_export find_reg_access(
|
||||
struct reg_access_t *out,
|
||||
ea_t start_ea,
|
||||
ea_t end_ea,
|
||||
const char *regname,
|
||||
int sflag);
|
||||
|
||||
//@}
|
||||
|
||||
class place_t;
|
||||
|
||||
|
||||
/// Search for a text substring (low level function).
|
||||
/// \param ud line array parameter
|
||||
/// \param[in,out] start pointer to starting place:
|
||||
/// - start->ea: starting address
|
||||
/// - start->lnnum: starting Y coordinate
|
||||
/// \param end pointer to ending place:
|
||||
/// - end->ea: ending address
|
||||
/// - end->lnnum: ending Y coordinate
|
||||
/// \param[in,out] startx pointer to starting X coordinate
|
||||
/// \param str substring to search for.
|
||||
/// \param sflag \ref SEARCH_
|
||||
/// \retval 0 substring not found
|
||||
/// \retval 1 substring found. The matching position is returned in:
|
||||
/// - start->ea: address
|
||||
/// - start->lnnum: Y coordinate
|
||||
/// - *startx: X coordinate
|
||||
/// \retval 2 search was cancelled by ctrl-break.
|
||||
/// The farthest searched address is
|
||||
/// returned in the same manner as in the successful return (1).
|
||||
/// \retval 3 the input regular expression is bad.
|
||||
/// The error message was displayed.
|
||||
|
||||
idaman int ida_export search(
|
||||
void *ud,
|
||||
place_t *start,
|
||||
const place_t *end,
|
||||
int *startx,
|
||||
const char *str,
|
||||
int sflag);
|
||||
|
||||
|
||||
#if !defined(NO_OBSOLETE_FUNCS)
|
||||
idaman DEPRECATED int ida_export user2bin(uchar *, uchar *, ea_t, const char *, int, bool); // use parse_binpat_str()
|
||||
idaman DEPRECATED ea_t ida_export find_binary(ea_t, ea_t, const char *, int, int); // use bin_search2()
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __SEARCH_HPP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SRAREA_HPP
|
||||
#define _SRAREA_HPP
|
||||
|
||||
#include <range.hpp>
|
||||
|
||||
/*! \file segregs.hpp
|
||||
|
||||
\brief Functions that deal with the segment registers.
|
||||
|
||||
If your processor doesn't use segment registers, then these functions
|
||||
are of no use for you. However, you should define
|
||||
two virtual segment registers - CS and DS (for code segment and
|
||||
data segment) and specify their internal numbers in the LPH structure
|
||||
(processor_t::reg_code_sreg and processor_t::reg_data_sreg).
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// The values of the segment registers are kept as address ranges. The segment
|
||||
/// register does not change its value within one address range.
|
||||
/// The processor module finds segment register change points and splits
|
||||
/// ::sreg_range_t ranges so that a new sreg_range_t range is started at
|
||||
/// each segment register change point. The kernel deletes sreg_range_t
|
||||
/// if an instruction is converted back to unexplored bytes.
|
||||
///
|
||||
/// So, we have information about a segment register by keeping information
|
||||
/// about the range of addresses where segment register does not change the value.
|
||||
///
|
||||
/// Note that each segment has information about the default values of
|
||||
/// the segment registers. This information is used if the value of a segment
|
||||
/// register could not be determined.
|
||||
struct sreg_range_t : public range_t
|
||||
{
|
||||
sel_t val; ///< segment register value
|
||||
uchar tag; ///< \ref SR_
|
||||
|
||||
/// \defgroup SR_ Segment register range tags
|
||||
/// Used by sreg_range_t::tag
|
||||
//@{
|
||||
#define SR_inherit 1 ///< the value is inherited from the previous range
|
||||
#define SR_user 2 ///< the value is specified by the user
|
||||
#define SR_auto 3 ///< the value is determined by IDA
|
||||
#define SR_autostart 4 ///< used as #SR_auto for segment starting address
|
||||
//@}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(sreg_range_t);
|
||||
|
||||
|
||||
/// Get value of a segment register.
|
||||
/// This function uses segment register range and default segment register
|
||||
/// values stored in the segment structure.
|
||||
/// \param ea linear address in the program
|
||||
/// \param rg number of the segment register
|
||||
/// \return value of the segment register, #BADSEL if value is unknown.
|
||||
|
||||
idaman sel_t ida_export get_sreg(ea_t ea, int rg);
|
||||
|
||||
|
||||
/// Create a new segment register range.
|
||||
/// This function is used when the IDP emulator detects that a segment
|
||||
/// register changes its value.
|
||||
/// \param ea linear address where the segment register will
|
||||
/// have a new value. if ea==#BADADDR, nothing to do.
|
||||
/// \param rg the number of the segment register
|
||||
/// \param v the new value of the segment register. If the value is
|
||||
/// unknown, you should specify #BADSEL.
|
||||
/// \param tag the register info tag. see \ref SR_
|
||||
/// \param silent if false, display a warning() in the case of failure
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export split_sreg_range(
|
||||
ea_t ea,
|
||||
int rg,
|
||||
sel_t v,
|
||||
uchar tag,
|
||||
bool silent=false);
|
||||
|
||||
|
||||
/// Set default value of a segment register for a segment.
|
||||
/// \param sg pointer to segment structure
|
||||
/// if NULL, then set the register for all segments
|
||||
/// \param rg number of segment register
|
||||
/// \param value its default value. this value will be used by get_sreg()
|
||||
/// if value of the register is unknown at the specified address.
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export set_default_sreg_value(segment_t *sg, int rg, sel_t value);
|
||||
|
||||
|
||||
/// Set the segment register value at the next instruction.
|
||||
/// This function is designed to be called from idb_event::sgr_changed handler
|
||||
/// in order to contain the effect of changing a segment
|
||||
/// register value only until the next instruction.
|
||||
///
|
||||
/// It is useful, for example, in the ARM module: the modification
|
||||
/// of the T register does not affect existing instructions later in the code.
|
||||
/// \param ea1 address to start to search for an instruction
|
||||
/// \param ea2 the maximal address
|
||||
/// \param rg the segment register number
|
||||
/// \param value the segment register value
|
||||
|
||||
idaman void ida_export set_sreg_at_next_code(ea_t ea1, ea_t ea2, int rg, sel_t value);
|
||||
|
||||
|
||||
/// Get segment register range by linear address.
|
||||
/// \param out segment register range
|
||||
/// \param ea any linear address in the program
|
||||
/// \param rg the segment register number
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export get_sreg_range(sreg_range_t *out, ea_t ea, int rg);
|
||||
|
||||
|
||||
/// Get segment register range previous to one with address.
|
||||
/// \note more efficient then get_sreg_range(reg, ea-1)
|
||||
/// \param out segment register range
|
||||
/// \param ea any linear address in the program
|
||||
/// \param rg the segment register number
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export get_prev_sreg_range(sreg_range_t *out, ea_t ea, int rg);
|
||||
|
||||
|
||||
/// Set default value of DS register for all segments
|
||||
|
||||
idaman void ida_export set_default_dataseg(sel_t ds_sel);
|
||||
|
||||
|
||||
/// Get number of segment register ranges.
|
||||
/// \param rg the segment register number
|
||||
|
||||
idaman size_t ida_export get_sreg_ranges_qty(int rg);
|
||||
|
||||
|
||||
/// Get segment register range by its number.
|
||||
/// \param out segment register range
|
||||
/// \param rg the segment register number
|
||||
/// \param n number of range (0..qty()-1)
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export getn_sreg_range(sreg_range_t *out, int rg, int n);
|
||||
|
||||
|
||||
/// Get number of segment register range by address.
|
||||
/// \param ea any address in the range
|
||||
/// \param rg the segment register number
|
||||
/// \return -1 if no range occupies the specified address.
|
||||
/// otherwise returns number of
|
||||
/// the specified range (0..get_srranges_qty()-1)
|
||||
|
||||
idaman int ida_export get_sreg_range_num(ea_t ea, int rg);
|
||||
|
||||
|
||||
/// Delete segment register range started at ea.
|
||||
/// When a segment register range is deleted,
|
||||
/// the previous range is extended to cover the empty space.
|
||||
/// The segment register range at the beginning of a segment cannot be deleted.
|
||||
/// \param ea start_ea of the deleted range
|
||||
/// \param rg the segment register number
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export del_sreg_range(ea_t ea, int rg);
|
||||
|
||||
|
||||
/// Duplicate segment register ranges.
|
||||
/// \param dst_rg number of destination segment register
|
||||
/// \param src_rg copy ranges from
|
||||
/// \param map_selector map selectors to linear addresses using sel2ea()
|
||||
|
||||
idaman void ida_export copy_sreg_ranges(int dst_rg, int src_rg, bool map_selector=false);
|
||||
|
||||
|
||||
#endif // _SRAREA_HPP
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* The Interactive Disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _STRLIST_HPP
|
||||
#define _STRLIST_HPP
|
||||
|
||||
/*! \file strlist.hpp
|
||||
|
||||
\brief Functions that deal with the string list
|
||||
|
||||
While the kernel keeps the strings list, it does not update it.
|
||||
The string list is not used by the kernel because
|
||||
keeping it up-to-date would slow down IDA without any benefit.
|
||||
|
||||
The users of this list should build_strlist() before accessing it.
|
||||
*/
|
||||
|
||||
/// Structure to keep string list parameters
|
||||
struct strwinsetup_t
|
||||
{
|
||||
strwinsetup_t()
|
||||
: minlen(-1), display_only_existing_strings(0),
|
||||
only_7bit(1), ignore_heads(0) {}
|
||||
bytevec_t strtypes; // set of allowed string types
|
||||
sval_t minlen;
|
||||
uchar display_only_existing_strings;
|
||||
uchar only_7bit;
|
||||
uchar ignore_heads;
|
||||
|
||||
inline bool is_initialized() const { return minlen > -1; }
|
||||
|
||||
};
|
||||
|
||||
/// Information about one string from the string list
|
||||
struct string_info_t
|
||||
{
|
||||
ea_t ea;
|
||||
int length; // in octets
|
||||
int type;
|
||||
string_info_t() : ea(BADADDR), length(0), type(0) {}
|
||||
string_info_t(ea_t _ea) : ea(_ea), length(0), type(0) {}
|
||||
bool operator<(const string_info_t &r) const { return ea < r.ea; }
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(string_info_t);
|
||||
|
||||
|
||||
/// Get access to the static string list options
|
||||
|
||||
idaman strwinsetup_t *ida_export get_strlist_options();
|
||||
|
||||
|
||||
/// Build the string list.
|
||||
/// You should initialize options before this call using the
|
||||
/// restore_config() or setup_strings_window() methods.
|
||||
|
||||
idaman void ida_export build_strlist();
|
||||
|
||||
|
||||
/// Clear the string list.
|
||||
|
||||
idaman void ida_export clear_strlist();
|
||||
|
||||
|
||||
/// Get number of elements in the string list
|
||||
|
||||
idaman size_t ida_export get_strlist_qty(void);
|
||||
|
||||
|
||||
/// Get nth element of the string list (n=0..get_strlist_qty()-1)
|
||||
|
||||
idaman bool ida_export get_strlist_item(string_info_t *si, size_t n);
|
||||
|
||||
|
||||
#endif // _STRLIST_HPP
|
||||
@@ -1,651 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \file struct.hpp
|
||||
|
||||
\brief Structure type management (assembly level types)
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _STRUCT_HPP
|
||||
#define _STRUCT_HPP
|
||||
#include <bytes.hpp>
|
||||
|
||||
#define STRUC_SEPARATOR '.' ///< structname.fieldname
|
||||
|
||||
/// Describes a member of an assembly level structure
|
||||
class member_t
|
||||
{
|
||||
public:
|
||||
tid_t id; ///< name(), cmt, rptcmt
|
||||
ea_t soff; ///< start offset (for unions - number of the member 0..n)
|
||||
ea_t eoff; ///< end offset
|
||||
flags_t flag; ///< type+representation bits
|
||||
//private:
|
||||
uint32 props; ///< \ref MF_
|
||||
/// \defgroup MF_ Struct member properties
|
||||
/// Used by member_t::props
|
||||
//@{
|
||||
#define MF_OK 0x00000001 ///< is the member ok? (always yes)
|
||||
#define MF_UNIMEM 0x00000002 ///< is a member of a union?
|
||||
#define MF_HASUNI 0x00000004 ///< has members of type "union"?
|
||||
#define MF_BYTIL 0x00000008 ///< the member was created due to the type system
|
||||
#define MF_HASTI 0x00000010 ///< has type information?
|
||||
#define MF_BASECLASS 0x00000020 ///< a special member representing base class
|
||||
#define MF_DTOR 0x00000040 ///< a special member representing destructor
|
||||
#define MF_DUPNAME 0x00000080 ///< duplicate name resolved with _N suffix (N==soff)
|
||||
//@}
|
||||
|
||||
/// Is a member of a union?
|
||||
bool unimem(void) const { return (props & MF_UNIMEM) != 0; }
|
||||
/// Has members of type "union"?
|
||||
bool has_union(void) const { return (props & MF_HASUNI) != 0; }
|
||||
/// Was the member created due to the type system?
|
||||
bool by_til(void) const { return (props & MF_BYTIL) != 0; }
|
||||
/// Has type information?
|
||||
bool has_ti(void) const { return (props & MF_HASTI) != 0; }
|
||||
/// Is a base class member?
|
||||
bool is_baseclass(void) const { return (props & MF_BASECLASS) != 0; }
|
||||
/// Duplicate name was resolved during import?
|
||||
bool is_dupname(void) const { return (props & MF_DUPNAME) != 0; }
|
||||
/// Is a virtual destructor?
|
||||
bool is_destructor(void) const { return (props & MF_DTOR) != 0; }
|
||||
/// Get start offset (for unions - returns 0)
|
||||
ea_t get_soff(void) const { return unimem() ? 0 : soff; }
|
||||
};
|
||||
|
||||
/// Information about a structure type (assembly level)
|
||||
//-V:struc_t:730 not all members of a class are initialized inside the constructor
|
||||
class struc_t
|
||||
{
|
||||
protected:
|
||||
struc_t(void) {} ///< plugins may not create struc_t instances. they should
|
||||
///< use add_struc() and get_struc()
|
||||
public:
|
||||
tid_t id; ///< struct id
|
||||
uint32 memqty; ///< number of members
|
||||
member_t *members; ///< only defined members are stored here.
|
||||
///< there may be gaps between members.
|
||||
ushort age; ///< not used
|
||||
uint32 props; ///< \ref SF_
|
||||
/// \defgroup SF_ Structure properties
|
||||
/// Used by struc_t::props
|
||||
//@{
|
||||
#define SF_VAR 0x00000001 ///< is variable size structure (varstruct)?
|
||||
///< a variable size structure is one with
|
||||
///< the zero size last member.
|
||||
///< if the last member is a varstruct, then the current
|
||||
///< structure is a varstruct too.
|
||||
#define SF_UNION 0x00000002 ///< is a union?
|
||||
///< varunions are prohibited!
|
||||
#define SF_HASUNI 0x00000004 ///< has members of type "union"?
|
||||
#define SF_NOLIST 0x00000008 ///< don't include in the chooser list
|
||||
#define SF_TYPLIB 0x00000010 ///< the structure comes from type library
|
||||
#define SF_HIDDEN 0x00000020 ///< the structure is collapsed
|
||||
#define SF_FRAME 0x00000040 ///< the structure is a function frame
|
||||
#define SF_ALIGN 0x00000F80 ///< alignment (shift amount: 0..31)
|
||||
#define SF_GHOST 0x00001000 ///< ghost copy of a local type
|
||||
//@}
|
||||
|
||||
/// Is variable size structure?
|
||||
bool is_varstr(void) const { return (props & SF_VAR) != 0; }
|
||||
/// Is a union?
|
||||
bool is_union(void) const { return (props & SF_UNION) != 0; }
|
||||
/// Has members of type "union"?
|
||||
bool has_union(void) const { return (props & SF_HASUNI) != 0; }
|
||||
/// Is included in chooser list?
|
||||
/// Use \ref set_struc_listed to change the listed status
|
||||
bool is_choosable(void) const { return (props & SF_NOLIST) == 0; }
|
||||
/// Does structure come from a type library?
|
||||
bool from_til(void) const { return (props & SF_TYPLIB) != 0; }
|
||||
/// Is the structure collapsed?
|
||||
/// Use \ref set_struc_hidden to change the hidden status
|
||||
bool is_hidden(void) const { return (props & SF_HIDDEN) != 0; }
|
||||
/// Is this structure a function frame?
|
||||
bool is_frame(void) const { return (props & SF_FRAME) != 0; }
|
||||
/// See #SF_ALIGN
|
||||
int get_alignment(void) const { return (props & SF_ALIGN) >> 7; }
|
||||
/// Is a ghost copy of a local type?
|
||||
bool is_ghost(void) const { return (props & SF_GHOST) != 0; }
|
||||
|
||||
/// Do not use; use set_struc_align()
|
||||
void set_alignment(int shift)
|
||||
{
|
||||
props &= ~SF_ALIGN;
|
||||
props |= (shift << 7) & SF_ALIGN;
|
||||
}
|
||||
|
||||
void set_ghost(bool _is_ghost) { setflag(props, SF_GHOST, _is_ghost); }
|
||||
|
||||
mutable int32 ordinal; ///< corresponding local type ordinal number
|
||||
};
|
||||
|
||||
/// \name Internal structures list
|
||||
/// IDA maintains an internal vector of known structures.
|
||||
/// Use these functions to work with this vector.
|
||||
//@{
|
||||
|
||||
/// Get number of known structures
|
||||
|
||||
idaman size_t ida_export get_struc_qty(void);
|
||||
|
||||
|
||||
/// Get index of first structure.
|
||||
/// \return #BADADDR if no known structures, 0 otherwise
|
||||
|
||||
idaman uval_t ida_export get_first_struc_idx(void);
|
||||
|
||||
|
||||
/// Get index of last structure.
|
||||
/// \return #BADADDR if no known structures, get_struc_qty()-1 otherwise
|
||||
|
||||
idaman uval_t ida_export get_last_struc_idx(void);
|
||||
|
||||
|
||||
/// Get previous struct index.
|
||||
/// \return #BADADDR if resulting index is negative, otherwise idx - 1
|
||||
|
||||
inline THREAD_SAFE uval_t get_prev_struc_idx(uval_t idx) { return idx == BADNODE ? idx : idx - 1; }
|
||||
|
||||
|
||||
/// Get next struct index.
|
||||
/// \return #BADADDR if resulting index is out of bounds, otherwise idx++
|
||||
|
||||
idaman uval_t ida_export get_next_struc_idx(uval_t idx);
|
||||
|
||||
|
||||
/// Get internal number of the structure
|
||||
|
||||
idaman uval_t ida_export get_struc_idx(tid_t id);
|
||||
|
||||
|
||||
/// Get struct id by struct number
|
||||
|
||||
idaman tid_t ida_export get_struc_by_idx(uval_t idx);
|
||||
|
||||
|
||||
/// Get pointer to struct type info
|
||||
|
||||
idaman struc_t *ida_export get_struc(tid_t id);
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/// Get struct id by name
|
||||
|
||||
inline tid_t get_struc_id(const char *name)
|
||||
{
|
||||
tid_t id = node2ea(netnode(name));
|
||||
return get_struc(id) == NULL ? BADADDR : id;
|
||||
}
|
||||
|
||||
|
||||
/// Get struct name by id
|
||||
/// \param[out] out buffer to hold the name
|
||||
/// \param id struct id
|
||||
/// \param flags \ref STRNFL_
|
||||
|
||||
idaman ssize_t ida_export get_struc_name(qstring *out, tid_t id, int flags=0);
|
||||
|
||||
/// \defgroup STRNFL_ Struct name flags
|
||||
/// Passed as 'flags' parameter to get_struc_name()
|
||||
//@{
|
||||
#define STRNFL_REGEX 0x0001 ///< apply regular expressions to beautify the name
|
||||
//@}
|
||||
|
||||
inline qstring get_struc_name(tid_t id, int flags=0)
|
||||
{
|
||||
qstring name;
|
||||
get_struc_name(&name, id, flags);
|
||||
return name;
|
||||
}
|
||||
|
||||
/// Get struct comment
|
||||
|
||||
inline ssize_t get_struc_cmt(qstring *buf, tid_t id, bool repeatable) { return getnode(id).supstr(buf, repeatable != 0); }
|
||||
|
||||
|
||||
/// Get struct size (also see get_struc_size(tid_t))
|
||||
|
||||
idaman asize_t ida_export get_struc_size(const struc_t *sptr);
|
||||
|
||||
/// Get struct size (also see get_struc_size(const struc_t *))
|
||||
|
||||
inline asize_t get_struc_size(tid_t id) { return get_struc_size(get_struc(id)); }
|
||||
|
||||
|
||||
/// \name Struct offsets
|
||||
/// \note for unions, soff == number of the current member
|
||||
//@{
|
||||
|
||||
/// Get offset of member with largest offset less than 'offset'.
|
||||
/// \return #BADADDR if no prev offset
|
||||
|
||||
idaman ea_t ida_export get_struc_prev_offset(const struc_t *sptr, ea_t offset);
|
||||
|
||||
|
||||
/// Get offset of member with smallest offset larger than 'offset'.
|
||||
/// \return #BADADDR if no next offset
|
||||
|
||||
idaman ea_t ida_export get_struc_next_offset(const struc_t *sptr, ea_t offset);
|
||||
|
||||
|
||||
/// Get offset of last member.
|
||||
/// \return #BADADDR if memqty == 0
|
||||
|
||||
idaman ea_t ida_export get_struc_last_offset(const struc_t *sptr);
|
||||
|
||||
|
||||
/// Get offset of first member.
|
||||
/// \return #BADADDR if memqty == 0
|
||||
|
||||
idaman ea_t ida_export get_struc_first_offset(const struc_t *sptr);
|
||||
|
||||
|
||||
/// For unions: returns number of members, for structs: returns size of structure
|
||||
|
||||
inline ea_t get_max_offset(struc_t *sptr)
|
||||
{
|
||||
if ( sptr == NULL )
|
||||
return 0; // just to avoid GPF
|
||||
return sptr->is_union()
|
||||
? sptr->memqty
|
||||
: get_struc_size(sptr);
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/// Is variable size structure?
|
||||
|
||||
inline bool is_varstr(tid_t id)
|
||||
{
|
||||
struc_t *sptr = get_struc(id);
|
||||
return sptr != NULL && sptr->is_varstr();
|
||||
}
|
||||
|
||||
/// Is a union?
|
||||
|
||||
inline bool is_union(tid_t id)
|
||||
{
|
||||
struc_t *sptr = get_struc(id);
|
||||
return sptr != NULL && sptr->is_union();
|
||||
}
|
||||
|
||||
|
||||
/// Get containing structure of member by its full name "struct.field"
|
||||
|
||||
idaman struc_t *ida_export get_member_struc(const char *fullname);
|
||||
|
||||
|
||||
/// Get child struct if member is a struct
|
||||
|
||||
idaman struc_t *ida_export get_sptr(const member_t *mptr);
|
||||
|
||||
|
||||
/// Get member at given offset
|
||||
|
||||
idaman member_t *ida_export get_member(const struc_t *sptr, asize_t offset);
|
||||
|
||||
/// Get member id at given offset
|
||||
|
||||
inline tid_t get_member_id(const struc_t *sptr, asize_t offset)
|
||||
{
|
||||
member_t *mptr = get_member(sptr, offset);
|
||||
return mptr != NULL ? mptr->id : BADADDR;
|
||||
}
|
||||
|
||||
|
||||
/// Get a member by its name, like "field44"
|
||||
|
||||
idaman member_t *ida_export get_member_by_name(const struc_t *sptr, const char *membername);
|
||||
|
||||
|
||||
/// Get a member by its fully qualified name, "struct.field"
|
||||
|
||||
idaman member_t *ida_export get_member_by_fullname(struc_t **sptr_place, const char *fullname);
|
||||
|
||||
|
||||
/// Get a member's fully qualified name, "struct.field"
|
||||
|
||||
inline ssize_t idaapi get_member_fullname(qstring *out, tid_t mid) { return getnode(mid).get_name(out); }
|
||||
|
||||
|
||||
/// Get name of structure member
|
||||
|
||||
idaman ssize_t ida_export get_member_name(qstring *out, tid_t mid);
|
||||
|
||||
inline qstring get_member_name(tid_t mid)
|
||||
{
|
||||
qstring name;
|
||||
get_member_name(&name, mid);
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/// Get comment of structure member
|
||||
|
||||
inline ssize_t idaapi get_member_cmt(qstring *buf, tid_t mid, bool repeatable) { return getnode(mid).supstr(buf, repeatable != 0); }
|
||||
|
||||
|
||||
/// Get size of structure member.
|
||||
/// May return 0 for the last member of varstruct.
|
||||
/// For union members, returns member_t::eoff.
|
||||
|
||||
inline asize_t get_member_size(const member_t *NONNULL mptr) { return mptr->unimem() ? mptr->eoff : (mptr->eoff - mptr->soff); }
|
||||
|
||||
|
||||
/// Is variable size member?
|
||||
|
||||
idaman bool ida_export is_varmember(const member_t *mptr);
|
||||
|
||||
|
||||
/// Get member that is most likely referenced by the specified offset.
|
||||
/// Useful for offsets > sizeof(struct).
|
||||
|
||||
idaman member_t *ida_export get_best_fit_member(const struc_t *sptr, asize_t offset);
|
||||
|
||||
|
||||
/// Get the next member idx, if it does not exist, return -1
|
||||
|
||||
idaman ssize_t ida_export get_next_member_idx(const struc_t *sptr, asize_t off);
|
||||
|
||||
|
||||
/// Get the prev member idx, if it does not exist, return -1
|
||||
|
||||
idaman ssize_t ida_export get_prev_member_idx(const struc_t *sptr, asize_t off);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// manipulation
|
||||
|
||||
/// Create a structure type.
|
||||
/// if idx==#BADADDR then add as the last idx.
|
||||
/// if name==NULL then a name will be generated "struct_%d".
|
||||
|
||||
idaman tid_t ida_export add_struc(uval_t idx, const char *name, bool is_union=false);
|
||||
|
||||
|
||||
/// Delete a structure type
|
||||
|
||||
idaman bool ida_export del_struc(struc_t *sptr);
|
||||
|
||||
|
||||
/// Set internal number of struct.
|
||||
/// Also see get_struc_idx(), get_struc_by_idx().
|
||||
|
||||
idaman bool ida_export set_struc_idx(const struc_t *sptr, uval_t idx);
|
||||
|
||||
|
||||
/// Set structure alignment (#SF_ALIGN)
|
||||
|
||||
idaman bool ida_export set_struc_align(struc_t *sptr, int shift);
|
||||
|
||||
|
||||
/// Set structure name
|
||||
|
||||
idaman bool ida_export set_struc_name(tid_t id, const char *name);
|
||||
|
||||
|
||||
/// Set structure comment
|
||||
|
||||
idaman bool ida_export set_struc_cmt(tid_t id, const char *cmt, bool repeatable);
|
||||
|
||||
|
||||
/// Return values for add_struc_member()
|
||||
enum struc_error_t
|
||||
{
|
||||
STRUC_ERROR_MEMBER_OK = 0, ///< success
|
||||
STRUC_ERROR_MEMBER_NAME = -1, ///< already has member with this name (bad name)
|
||||
STRUC_ERROR_MEMBER_OFFSET = -2, ///< already has member at this offset
|
||||
STRUC_ERROR_MEMBER_SIZE = -3, ///< bad number of bytes or bad sizeof(type)
|
||||
STRUC_ERROR_MEMBER_TINFO = -4, ///< bad typeid parameter
|
||||
STRUC_ERROR_MEMBER_STRUCT = -5, ///< bad struct id (the 1st argument)
|
||||
STRUC_ERROR_MEMBER_UNIVAR = -6, ///< unions can't have variable sized members
|
||||
STRUC_ERROR_MEMBER_VARLAST = -7, ///< variable sized member should be the last member in the structure
|
||||
STRUC_ERROR_MEMBER_NESTED = -8, ///< recursive structure nesting is forbidden
|
||||
};
|
||||
|
||||
|
||||
/// Add member to existing structure.
|
||||
/// \param sptr structure to modify
|
||||
/// \param fieldname if NULL, then "anonymous_#" name will be generated
|
||||
/// \param offset #BADADDR means add to the end of structure
|
||||
/// \param flag type + representation bits
|
||||
/// \param mt additional info about member type.
|
||||
/// must be present for
|
||||
/// structs, offsets, enums, strings,
|
||||
/// struct offsets.
|
||||
/// \param nbytes if == 0 then the structure
|
||||
/// will be a varstruct.
|
||||
/// in this case the member should be
|
||||
/// the last member in the structure
|
||||
|
||||
idaman struc_error_t ida_export add_struc_member(
|
||||
struc_t *sptr,
|
||||
const char *fieldname,
|
||||
ea_t offset,
|
||||
flags_t flag,
|
||||
const opinfo_t *mt,
|
||||
asize_t nbytes);
|
||||
|
||||
|
||||
/// Delete member at given offset
|
||||
|
||||
idaman bool ida_export del_struc_member(struc_t *sptr, ea_t offset);
|
||||
|
||||
|
||||
/// Delete members which occupy range of offsets (off1..off2).
|
||||
/// \return number of deleted members or -1 on error
|
||||
|
||||
idaman int ida_export del_struc_members(struc_t *sptr, ea_t off1, ea_t off2);
|
||||
|
||||
|
||||
/// Set name of member at given offset
|
||||
|
||||
idaman bool ida_export set_member_name(struc_t *sptr, ea_t offset,const char *name);
|
||||
|
||||
|
||||
/// Set type of member at given offset (also see add_struc_member())
|
||||
|
||||
idaman bool ida_export set_member_type(struc_t *sptr, ea_t offset, flags_t flag,const opinfo_t *mt, asize_t nbytes);
|
||||
|
||||
|
||||
/// Set member comment
|
||||
|
||||
idaman bool ida_export set_member_cmt(member_t *mptr,const char *cmt, bool repeatable);
|
||||
|
||||
|
||||
/// Expand/Shrink structure type
|
||||
|
||||
idaman bool ida_export expand_struc(struc_t *sptr, ea_t offset, adiff_t delta, bool recalc=true);
|
||||
|
||||
|
||||
/// Update struct information in the database (internal function)
|
||||
|
||||
idaman void ida_export save_struc(struc_t *sptr, bool may_update_ltypes=true);
|
||||
|
||||
|
||||
/// Hide/unhide a struct type
|
||||
inline void idaapi set_struc_hidden(struc_t *sptr, bool is_hidden)
|
||||
{
|
||||
setflag(sptr->props, SF_HIDDEN, is_hidden);
|
||||
save_struc(sptr, false);
|
||||
}
|
||||
|
||||
/// Add/remove a struct type from the struct list
|
||||
inline void idaapi set_struc_listed(struc_t *sptr, bool is_listed)
|
||||
{
|
||||
setflag(sptr->props, SF_NOLIST, !is_listed);
|
||||
save_struc(sptr, false);
|
||||
}
|
||||
|
||||
/// Member type information (return values for set_member_tinfo())
|
||||
enum smt_code_t
|
||||
{
|
||||
SMT_BADARG = -6, ///< bad parameters
|
||||
SMT_NOCOMPAT = -5, ///< the new type is not compatible with the old type
|
||||
SMT_WORSE = -4, ///< the new type is worse than the old type
|
||||
SMT_SIZE = -3, ///< the new type is incompatible with the member size
|
||||
SMT_ARRAY = -2, ///< arrays are forbidden as function arguments
|
||||
SMT_OVERLAP = -1, ///< member would overlap with members that cannot be deleted
|
||||
SMT_FAILED = 0, ///< failed to set new member type
|
||||
SMT_OK = 1, ///< success: changed the member type
|
||||
SMT_KEEP = 2, ///< no need to change the member type, the old type is better
|
||||
};
|
||||
|
||||
|
||||
/// Get tinfo for given member
|
||||
|
||||
idaman bool ida_export get_member_tinfo(tinfo_t *tif, const member_t *mptr);
|
||||
|
||||
|
||||
/// Delete tinfo for given member
|
||||
|
||||
idaman bool ida_export del_member_tinfo(struc_t *sptr, member_t *mptr);
|
||||
|
||||
|
||||
/// Set tinfo for given member.
|
||||
/// \param sptr containing struct
|
||||
/// \param mptr target member
|
||||
/// \param memoff offset within member
|
||||
/// \param tif type info
|
||||
/// \param flags \ref SET_MEMTI_
|
||||
|
||||
idaman smt_code_t ida_export set_member_tinfo(
|
||||
struc_t *sptr,
|
||||
member_t *mptr,
|
||||
uval_t memoff,
|
||||
const tinfo_t &tif,
|
||||
int flags);
|
||||
|
||||
/// \defgroup SET_MEMTI_ Set member tinfo flags
|
||||
/// Passed as 'flags' parameter to set_member_tinfo()
|
||||
//@{
|
||||
#define SET_MEMTI_MAY_DESTROY 0x0001 ///< may destroy other members
|
||||
#define SET_MEMTI_COMPATIBLE 0x0002 ///< new type must be compatible with the old
|
||||
#define SET_MEMTI_FUNCARG 0x0004 ///< mptr is function argument (cannot create arrays)
|
||||
#define SET_MEMTI_BYTIL 0x0008 ///< new type was created by the type subsystem
|
||||
#define SET_MEMTI_USERTI 0x0010 ///< user-specified type
|
||||
//@}
|
||||
|
||||
|
||||
/// Try to get tinfo for given member - if failed, generate a tinfo using information about the
|
||||
/// member id from the disassembly
|
||||
|
||||
idaman bool ida_export get_or_guess_member_tinfo(tinfo_t *tif, const member_t *mptr);
|
||||
|
||||
|
||||
/// Get operand type info for member
|
||||
|
||||
inline opinfo_t *retrieve_member_info(opinfo_t *buf, const member_t *mptr)
|
||||
{
|
||||
if ( mptr == NULL )
|
||||
return NULL;
|
||||
return get_opinfo(buf, mptr->id, 0, mptr->flag);
|
||||
}
|
||||
|
||||
|
||||
/// Is member name prefixed with "anonymous"?
|
||||
|
||||
inline THREAD_SAFE bool is_anonymous_member_name(const char *name)
|
||||
{
|
||||
return name == NULL
|
||||
|| strncmp(name, "anonymous", 9) == 0;
|
||||
}
|
||||
|
||||
|
||||
/// Is member name an auto-generated name?
|
||||
|
||||
inline THREAD_SAFE bool is_dummy_member_name(const char *name)
|
||||
{
|
||||
return name == NULL
|
||||
|| strncmp(name, "arg_", 4) == 0
|
||||
|| strncmp(name, "var_", 4) == 0
|
||||
|| is_anonymous_member_name(name);
|
||||
}
|
||||
|
||||
|
||||
/// Check if the specified member id points to a struct member
|
||||
|
||||
inline member_t *idaapi get_member_by_id(
|
||||
qstring *out_mname, // note: id 'out_mname' is important for SWiG
|
||||
tid_t mid,
|
||||
struc_t **sptr_place)
|
||||
{
|
||||
if ( get_member_fullname(out_mname, mid) > 0 )
|
||||
return get_member_by_fullname(sptr_place, out_mname->begin());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Check if the specified member id points to a struct member. convenience function
|
||||
|
||||
inline member_t *idaapi get_member_by_id(tid_t mid, struc_t **sptr_place=NULL)
|
||||
{
|
||||
qstring buf;
|
||||
return get_member_by_id(&buf, mid, sptr_place);
|
||||
}
|
||||
|
||||
|
||||
/// Is a member id?
|
||||
|
||||
inline bool is_member_id(tid_t mid)
|
||||
{
|
||||
return get_member_by_id(mid) != NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Is a special member with the name beginning with ' '?
|
||||
|
||||
idaman bool ida_export is_special_member(tid_t id);
|
||||
|
||||
|
||||
/// Implements action to take when a field is visited with visit_stroff_fields()
|
||||
struct ida_local struct_field_visitor_t
|
||||
{
|
||||
virtual int idaapi visit_field(struc_t *sptr, member_t *mptr) = 0;
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Visit structure fields in a stroff expression or in a reference to a struct data variable.
|
||||
/// This function can be used to enumerate all components of an expression like 'a.b.c'.
|
||||
/// \param sfv visitor object
|
||||
/// \param path struct path (path[0] contains the initial struct id)
|
||||
/// \param plen len
|
||||
/// \param[in,out] disp offset into structure
|
||||
/// \param appzero should visit field at offset zero?
|
||||
|
||||
idaman flags_t ida_export visit_stroff_fields(
|
||||
struct_field_visitor_t &sfv,
|
||||
const tid_t *path,
|
||||
int plen,
|
||||
adiff_t *disp,
|
||||
bool appzero);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Should display a structure offset expression as the structure size?
|
||||
|
||||
inline bool stroff_as_size(int plen, const struc_t *sptr, asize_t value)
|
||||
{
|
||||
return plen == 1
|
||||
&& value > 0
|
||||
&& sptr != NULL
|
||||
&& !sptr->is_varstr()
|
||||
&& value == get_struc_size(sptr);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// F U N C T I O N S F O R T H E K E R N E L
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------\cond
|
||||
inline void save_structs(void) {}
|
||||
///----------------------------------------------------------------\endcond
|
||||
|
||||
|
||||
#endif // _STRUCT_HPP
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 2016-2020 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
* Module independent exception description
|
||||
*/
|
||||
|
||||
#ifndef TRYBLKS_HPP
|
||||
#define TRYBLKS_HPP
|
||||
|
||||
/*! \file tryblks.hpp
|
||||
*
|
||||
* \brief Architecture independent exception handling info.
|
||||
*
|
||||
* Try blocks have the following general properties:
|
||||
* - An try block specifies a possibly fragmented guarded code region.
|
||||
* - Each try block has always at least one catch/except block description
|
||||
* - Each catch block contains its boundaries and a filter.
|
||||
* - Additionally a catch block can hold sp adjustment and the offset to the
|
||||
* exception object offset (C++).
|
||||
* - Try blocks can be nested. Nesting is automatically calculated at the retrieval time.
|
||||
* - There may be (nested) multiple try blocks starting at the same address.
|
||||
*
|
||||
* See examples in tests/input/src/eh_tests.
|
||||
*
|
||||
*/
|
||||
|
||||
// We use end_ea=BADADDR if the exact boundaries are unknown of any range.
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// An exception handler clause (the body of __except or catch statement)
|
||||
struct try_handler_t : public rangevec_t
|
||||
{
|
||||
sval_t disp; // displacement to the stack region of the guarded region.
|
||||
// if it is valid, it is fpreg relative.
|
||||
// -1 means unknown.
|
||||
int fpreg; // frame register number used in handler. -1 means none.
|
||||
|
||||
try_handler_t() : disp(-1), fpreg(-1) {}
|
||||
void clear(void)
|
||||
{
|
||||
rangevec_t::clear();
|
||||
disp = -1;
|
||||
fpreg = -1;
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(try_handler_t);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// __except() {} statement
|
||||
struct seh_t : public try_handler_t
|
||||
{
|
||||
rangevec_t filter; // boundaries of the filter callback. if filter is empty,
|
||||
ea_t seh_code; // then use seh_code
|
||||
#define SEH_CONTINUE BADADDR // EXCEPTION_CONTINUE_EXECUTION (-1)
|
||||
#define SEH_SEARCH ea_t(0) // EXCEPTION_CONTINUE_SEARCH (0) (alias of __finally)
|
||||
#define SEH_HANDLE ea_t(1) // EXCEPTION_EXECUTE_HANDLER (1)
|
||||
void clear(void)
|
||||
{
|
||||
try_handler_t::clear();
|
||||
filter.clear();
|
||||
seh_code = SEH_CONTINUE;
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(seh_t);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// catch() {} statement
|
||||
struct catch_t : public try_handler_t
|
||||
{
|
||||
sval_t obj; // fpreg relative displacement to the exception object. -1 if unknown.
|
||||
sval_t type_id; // the type caught by this catch. -1 means "catch(...)"
|
||||
#define CATCH_ID_ALL sval_t(-1) // catch(...)
|
||||
#define CATCH_ID_CLEANUP sval_t(-2) // a cleanup handler invoked if exception occures
|
||||
|
||||
catch_t() : obj(-1), type_id(-1) {}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(catch_t);
|
||||
typedef qvector<catch_t> catchvec_t;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class tryblk_t : public rangevec_t // block guarded by try/__try {...} statements
|
||||
{
|
||||
#ifndef SWIG
|
||||
char reserve[qmax(sizeof(catchvec_t), sizeof(seh_t))]; // seh_t or catchvec_t
|
||||
#endif
|
||||
uchar cb; // size of tryblk_t
|
||||
uchar kind; // one of the following kinds
|
||||
#define TB_NONE 0 // empty
|
||||
#define TB_SEH 1 // MS SEH __try/__except/__finally
|
||||
#define TB_CPP 2 // C++ language try/catch
|
||||
|
||||
public:
|
||||
uchar level; // nesting level, calculated by get_tryblks()
|
||||
|
||||
// C++ try/catch block (TB_CPP)
|
||||
catchvec_t &cpp() { return *(( catchvec_t *)reserve); }
|
||||
const catchvec_t &cpp() const { return *((const catchvec_t *)reserve); }
|
||||
|
||||
// SEH __except/__finally case (TB_SEH)
|
||||
seh_t &seh() { return *(( seh_t *)reserve); }
|
||||
const seh_t &seh() const { return *((const seh_t *)reserve); }
|
||||
|
||||
tryblk_t() : rangevec_t(), cb(sizeof(*this)), kind(TB_NONE), level(0) { reserve[0] = '\0'; }
|
||||
~tryblk_t() { clear(); }
|
||||
tryblk_t(const tryblk_t &r) : rangevec_t(), kind(TB_NONE) { *this = r; }
|
||||
uchar get_kind(void) const { return kind; }
|
||||
bool empty(void) const { return kind == TB_NONE; }
|
||||
bool is_seh(void) const { return kind == TB_SEH; }
|
||||
bool is_cpp(void) const { return kind == TB_CPP; }
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
tryblk_t &operator=(const tryblk_t &r)
|
||||
{
|
||||
if ( this != &r ) // don't copy yourself
|
||||
{
|
||||
if ( kind != TB_NONE )
|
||||
clear();
|
||||
kind = r.kind;
|
||||
level = r.level;
|
||||
rangevec_t::operator=(r);
|
||||
|
||||
if ( kind == TB_SEH )
|
||||
new (reserve) seh_t(r.seh());
|
||||
else if ( kind == TB_CPP )
|
||||
new (reserve) catchvec_t(r.cpp());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void clear(void)
|
||||
{
|
||||
if ( kind == TB_CPP )
|
||||
cpp().~catchvec_t();
|
||||
else if ( kind == TB_SEH )
|
||||
seh().~seh_t();
|
||||
kind = TB_NONE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
seh_t &set_seh(void)
|
||||
{
|
||||
if ( kind != TB_SEH )
|
||||
{
|
||||
clear();
|
||||
new (reserve) seh_t;
|
||||
kind = TB_SEH;
|
||||
}
|
||||
else
|
||||
{
|
||||
seh().clear();
|
||||
}
|
||||
return seh();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
catchvec_t &set_cpp(void)
|
||||
{
|
||||
if ( kind != TB_CPP )
|
||||
{
|
||||
clear();
|
||||
new (reserve) catchvec_t;
|
||||
kind = TB_CPP;
|
||||
}
|
||||
else
|
||||
{
|
||||
cpp().clear();
|
||||
}
|
||||
return cpp();
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(tryblk_t);
|
||||
typedef qvector<tryblk_t> tryblks_t;
|
||||
|
||||
///-------------------------------------------------------------------------
|
||||
/// Retrieve try block information from the specified address range.
|
||||
/// Try blocks are sorted by starting address and their nest levels calculated.
|
||||
/// \param tbv output buffer; may be NULL
|
||||
/// \param range address range to change
|
||||
/// \return number of found try blocks
|
||||
|
||||
idaman size_t ida_export get_tryblks(tryblks_t *tbv, const range_t &range);
|
||||
|
||||
/// Delete try block information in the specified range.
|
||||
/// \param range the range to be cleared
|
||||
|
||||
idaman void ida_export del_tryblks(const range_t &range);
|
||||
|
||||
|
||||
/// Add one try block information.
|
||||
/// \param tb try block to add.
|
||||
/// \return error code; 0 means good
|
||||
|
||||
idaman int ida_export add_tryblk(const tryblk_t &tb);
|
||||
|
||||
/// \defgroup TBERR_ Try block handling error codes
|
||||
//@{
|
||||
#define TBERR_OK 0 ///< ok
|
||||
#define TBERR_START 1 ///< bad start address
|
||||
#define TBERR_END 2 ///< bad end address
|
||||
#define TBERR_ORDER 3 ///< bad address order
|
||||
#define TBERR_EMPTY 4 ///< empty try block
|
||||
#define TBERR_KIND 5 ///< illegal try block kind
|
||||
#define TBERR_NO_CATCHES 6 ///< no catch blocks at all
|
||||
#define TBERR_INTERSECT 7 ///< range would intersect inner tryblk
|
||||
//@}
|
||||
|
||||
/// Find the start address of the system eh region including the argument.
|
||||
/// \param ea search address
|
||||
/// \return start address of surrounding tryblk, otherwise BADADDR
|
||||
|
||||
idaman ea_t ida_export find_syseh(ea_t ea);
|
||||
|
||||
|
||||
#endif // TRYBLKS_HPP
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user