Merge 'dev' branch into stable

This commit is contained in:
Anton Kochkov 2023-02-22 21:24:58 +08:00
commit 341d42c3a2
89 changed files with 2507 additions and 3284 deletions

View File

@ -30,7 +30,6 @@ install:
before_build:
- cmd: git submodule update --init --recursive
- scripts\prepare_breakpad.bat
# Build config
build_script:
@ -48,7 +47,6 @@ build_script:
-DCUTTER_PACKAGE_RZ_GHIDRA=ON
-DCUTTER_PACKAGE_JSDEC=ON
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON
-DCUTTER_ENABLE_CRASH_REPORTS=ON
-DCMAKE_PREFIX_PATH=%CUTTER_DEPS%\\pyside
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME%
-G Ninja

View File

@ -2,9 +2,9 @@ blank_issues_enabled: false
contact_links:
- name: Questions Telegram
url: https://t.me/r2cutter
url: https://t.me/cutter_re
about: Please ask questions about Cutter here or one of the other community channels, not in the issue tracker.
- name: Questions IRC
url: https://web.libera.chat/#cutter
about: "#cutter on https://web.libera.chat/"
about: "#cutter on https://web.libera.chat/"

View File

@ -23,6 +23,7 @@ jobs:
name: [
linux-x86_64,
linux-x86_64-system-deps,
linux-x86_64-qt6-system-deps,
macos-x86_64,
windows-x86_64,
tarball
@ -39,6 +40,12 @@ jobs:
system-deps: true
cc-override: '/usr/bin/gcc-7'
cxx-override: '/usr/bin/g++-7'
- name: linux-x86_64-qt6-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 22.04 using sytem libraries
os: ubuntu-22.04
python-version: 3.10.x
system-deps: true
cc-override: '/usr/bin/gcc-12'
cxx-override: '/usr/bin/g++-12'
- name: linux-x86_64
os: ubuntu-18.04
python-version: 3.7.x
@ -61,7 +68,7 @@ jobs:
# Prevent one job from pausing the rest
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
persist-credentials: false
@ -69,32 +76,34 @@ jobs:
if: contains(matrix.os, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install libgraphviz-dev mesa-common-dev libxkbcommon-x11-dev libclang-8-dev llvm-8 ninja-build
if [[ "${{ matrix.os }}" = "ubuntu-18.04" ]]
sudo apt-get install libgraphviz-dev mesa-common-dev libxkbcommon-x11-dev ninja-build
if [[ "${{ matrix.os }}" = "ubuntu-18.04" || "${{ matrix.os }}" = "ubuntu-20.04" ]]
then
# install additional packages needed for appimage
sudo apt-get install libxcb1-dev libxkbcommon-dev libxcb-*-dev libegl1
sudo apt-get install libxcb1-dev libxkbcommon-dev libxcb-*-dev libegl1 libclang-8-dev llvm-8
fi
if [[ "${{ matrix.system-deps }}" = "true" ]]
if [[ "${{ matrix.os }}" = "ubuntu-18.04" && "${{ matrix.system-deps }}" = "true" ]]
then
sudo apt-get install qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools
fi
- uses: actions/setup-python@v1
if [[ "${{ matrix.os }}" = "ubuntu-22.04" ]]
then
sudo apt-get install libclang-12-dev llvm-12 qt6-base-dev qt6-tools-dev \
qt6-tools-dev-tools libqt6svg6-dev libqt6core5compat6-dev libqt6svgwidgets6 qt6-l10n-tools
fi
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: homebrew dependencies
if: contains(matrix.os, 'macos')
run: |
cd scripts
rm '/usr/local/bin/2to3' # symlink to some kind of existing python2.7 installation conflicts with brew python3 which gets installed as indirect dependency
brew update --preinstall # temporary workaround for https://github.com/Homebrew/homebrew-bundle/issues/751
rm /usr/local/bin/2to3* # symlink to some kind of existing python2.7 installation conflicts with brew python3 which gets installed as indirect dependency
brew bundle
brew install coreutils
brew install pkg-config
- name: py dependencies
run: |
python3 -m pip install -U pip==21.3.1
pip install meson
pip install meson==0.61.5 # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true
- name: Prepare package id
shell: bash
run: |
@ -123,8 +132,6 @@ jobs:
export CXX="${{matrix.cxx-override}}"
fi
source scripts/prepare_breakpad_linux.sh
export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" #
mkdir build
cd build
cmake --version
@ -138,8 +145,7 @@ jobs:
-DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
-DCUTTER_ENABLE_GRAPHVIZ=OFF \
-DCUTTER_ENABLE_CRASH_REPORTS=ON \
-DCUTTER_ENABLE_GRAPHVIZ=ON \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
-DCUTTER_APPIMAGE_BUILD=ON \
-DCUTTER_ENABLE_PACKAGING=ON \
@ -152,6 +158,14 @@ jobs:
-DCMAKE_INSTALL_PREFIX=appdir/usr \
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \
..
elif [[ "${{ matrix.os }}" = "ubuntu-22.04" ]]
then
cmake \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCUTTER_QT6=ON \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
..
else
cmake \
-G Ninja \
@ -194,7 +208,6 @@ jobs:
source cutter-deps/env.sh
set -euo pipefail
export PATH=/usr/local/opt/llvm/bin:$PATH
source scripts/prepare_breakpad_macos.sh
mkdir build
cd build
PACKAGE_NAME=Cutter-${PACKAGE_ID}-macOS-x86_64
@ -205,7 +218,6 @@ jobs:
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
-DCUTTER_ENABLE_PYTHON=ON \
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
-DCUTTER_ENABLE_CRASH_REPORTS=ON \
-DCUTTER_USE_BUNDLED_RIZIN=ON \
-DCUTTER_ENABLE_PACKAGING=ON \
-DCUTTER_ENABLE_SIGDB=ON \
@ -216,7 +228,6 @@ jobs:
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \
-DCUTTER_PACKAGE_RZ_LIBYARA=ON \
-DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \
-DCMAKE_FRAMEWORK_PATH="$BREAKPAD_FRAMEWORK_DIR" \
-DCPACK_BUNDLE_APPLE_CERT_APP="-" \
.. && \
make -j4;
@ -240,7 +251,6 @@ jobs:
set CUTTER_DEPS=%CD%\cutter-deps
set PATH=%CD%\cutter-deps\qt\bin;%PATH%
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
call scripts\prepare_breakpad.bat
cd
mkdir build
cd build
@ -258,7 +268,6 @@ jobs:
-DCUTTER_PACKAGE_RZ_LIBYARA=ON ^
-DCUTTER_PACKAGE_JSDEC=ON ^
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^
-DCUTTER_ENABLE_CRASH_REPORTS=ON ^
-DCMAKE_PREFIX_PATH="%CUTTER_DEPS%\pyside" ^
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME% ^
-G Ninja ^
@ -276,7 +285,7 @@ jobs:
echo PACKAGE_NAME=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV
echo PACKAGE_PATH=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV
echo UPLOAD_ASSET_TYPE=application/gzip >> $GITHUB_ENV
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
if: env.PACKAGE_NAME != null
with:
name: ${{ env.PACKAGE_NAME }}
@ -284,7 +293,7 @@ jobs:
- name: Get release
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
id: get_release
uses: karliss/get-release@23b8b7144dd5b0c9d6942b2fb78bd9ae71546d03
uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release assets

View File

@ -7,16 +7,16 @@ jobs:
latest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-python@v1
- uses: actions/setup-python@v4
with:
python-version: 3.7.x
python-version: 3.9.x
- name: Download Coverity Build Tool
run: |
wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=radareorg%2Fcutter" -O cov-analysis-linux64.tar.gz
wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=rizinorg%2Fcutter" -O cov-analysis-linux64.tar.gz
mkdir cov-analysis-linux64
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
env:

View File

@ -7,9 +7,9 @@ on:
jobs:
deploy:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: recursive
- name: install dependencies

View File

@ -16,7 +16,7 @@ jobs:
outputs:
clang-format: ${{ steps.filter.outputs.clang-format }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: filter
with:
@ -33,7 +33,7 @@ jobs:
if: ${{ needs.changes.outputs.clang-format == 'true' }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install wget
run: sudo apt --assume-yes install wget

View File

@ -12,7 +12,6 @@ pipeline:
- export PACKAGE_ID=${CI_COMMIT_TAG=git-`date "+%Y-%m-%d"`-${CI_COMMIT_SHA}}
- export PACKAGE_NAME=Cutter-$${PACKAGE_ID}-macOS-arm64
- source cutter-deps/env.sh
- source scripts/prepare_breakpad_macos.sh
- cmake -Bbuild -GNinja
-DCMAKE_BUILD_TYPE=Release
-DPYTHON_LIBRARY="$$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.dylib"
@ -20,7 +19,6 @@ pipeline:
-DPYTHON_EXECUTABLE="$$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
-DCUTTER_ENABLE_PYTHON=ON
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
-DCUTTER_ENABLE_CRASH_REPORTS=ON
-DCUTTER_USE_BUNDLED_RIZIN=ON
-DCUTTER_ENABLE_PACKAGING=ON
-DCUTTER_ENABLE_SIGDB=ON
@ -31,7 +29,6 @@ pipeline:
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON
-DCUTTER_PACKAGE_RZ_LIBYARA=ON
-DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME"
-DCMAKE_FRAMEWORK_PATH="$$BREAKPAD_FRAMEWORK_DIR"
-DCPACK_BUNDLE_APPLE_CERT_APP="-"
- ninja -C build
package:

View File

@ -15,7 +15,6 @@ option(CUTTER_USE_ADDITIONAL_RIZIN_PATHS "Search rizin in additional paths which
Disable this option if you are linking against rizin pacakged as proper system library or in a custom path and additional are paths causing problems." ON)
option(CUTTER_ENABLE_PYTHON "Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}." OFF)
option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken2. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF)
option(CUTTER_ENABLE_CRASH_REPORTS "Enable crash report system. Unused if CUTTER_ENABLE_CRASH_REPORTS=OFF" OFF)
option(CUTTER_APPIMAGE_BUILD "Enable Appimage specific changes. Doesn't cause building of Appimage itself." OFF)
tri_option(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING "Use KSyntaxHighlighting" AUTO)
tri_option(CUTTER_ENABLE_GRAPHVIZ "Enable use of graphviz for graph layout" AUTO)
@ -26,8 +25,8 @@ option(CUTTER_ENABLE_PACKAGING "Enable building platform-specific packages for d
option(CUTTER_ENABLE_SIGDB "Downloads and installs sigdb (only available when CUTTER_USE_BUNDLED_RIZIN=ON)." OFF)
option(CUTTER_PACKAGE_DEPENDENCIES "During install step include the third party dependencies." OFF)
option(CUTTER_PACKAGE_RZ_GHIDRA "Compile and install rz-ghidra during install step." OFF)
option(CUTTER_PACKAGE_RZ_LIBSWIFT, "Compile and install rz-libswift demangler during the install step." OFF)
option(CUTTER_PACKAGE_RZ_LIBYARA, "Compile and install rz-libyara during the install step." OFF)
option(CUTTER_PACKAGE_RZ_LIBSWIFT "Compile and install rz-libswift demangler during the install step." OFF)
option(CUTTER_PACKAGE_RZ_LIBYARA "Compile and install rz-libyara during the install step." OFF)
option(CUTTER_PACKAGE_JSDEC "Compile and install jsdec during install step." OFF)
OPTION(CUTTER_QT6 "Use QT6" OFF)
@ -39,9 +38,33 @@ set(CUTTER_VERSION_MAJOR 2)
set(CUTTER_VERSION_MINOR 1)
set(CUTTER_VERSION_PATCH 2)
set(CUTTER_VERSION_FULL "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")
set(CUTTER_VERSION "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}")
project(Cutter VERSION "${CUTTER_VERSION_FULL}")
execute_process(COMMAND git log --pretty=format:'%h' -n 1
OUTPUT_VARIABLE GIT_REV
ERROR_QUIET)
# Check whether we got any revision (which isn't
# always the case, e.g. when someone downloaded a zip file
if ("${GIT_REV}" STREQUAL "")
set(CUTTER_VERSION_FULL "${CUTTER_VERSION}")
else()
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
OUTPUT_VARIABLE GIT_BRANCH)
string(STRIP "${GIT_REV}" GIT_REV)
string(SUBSTRING "${GIT_REV}" 1 7 GIT_REV)
string(STRIP "${GIT_BRANCH}" GIT_BRANCH)
set(CUTTER_VERSION_FULL "${CUTTER_VERSION}-${GIT_BRANCH}-${GIT_REV}")
endif()
project(Cutter VERSION "${CUTTER_VERSION}")
# Enable solution folder support
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# Put Qt files in a separate folder
set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP "Generated Files")
set(CMAKE_CXX_STANDARD 11)
@ -130,7 +153,6 @@ if(CUTTER_USE_BUNDLED_RIZIN)
endif()
message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}")
message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}")
message(STATUS "- Crash Handling: ${CUTTER_ENABLE_CRASH_REPORTS}")
message(STATUS "- KSyntaxHighlighting: ${KSYNTAXHIGHLIGHTING_STATUS}")
message(STATUS "- Graphviz: ${CUTTER_ENABLE_GRAPHVIZ}")
message(STATUS "- Downloads dependencies: ${CUTTER_ENABLE_DEPENDENCY_DOWNLOADS}")

View File

@ -6,7 +6,6 @@ Cutter is a free and open-source reverse engineering platform powered by [rizin]
[![Cutter CI](https://github.com/rizinorg/cutter/workflows/Cutter%20CI/badge.svg)](https://github.com/rizinorg/cutter/actions?query=workflow%3A%22Cutter+CI%22)
[![Build status](https://ci.appveyor.com/api/projects/status/tn7kttv55b8wf799/branch/dev?svg=true)](https://ci.appveyor.com/project/rizinorg/cutter/branch/dev)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/rizinorg/cutter.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/rizinorg/cutter/alerts/)
![Screenshot](https://raw.githubusercontent.com/rizinorg/cutter/dev/docs/source/images/screenshot.png)
@ -23,9 +22,11 @@ Cutter release binaries for all major platforms (Linux, macOS, Windows) can be d
- **macOS**: Download the `.dmg` file or use [Homebrew Cask](https://github.com/Homebrew/homebrew-cask):
`brew install --cask cutter`
- **Windows**: Download the `.zip` archive or use [Chocolatey](https://chocolatey.org):
- **Windows**: Download the `.zip` archive, or use either [Chocolatey](https://chocolatey.org) or [Scoop](https://scoop.sh/):
`choco install cutter`
`scoop bucket add extras` followed by `scoop install cutter`
### Build from sources

View File

@ -40,6 +40,7 @@ ExternalProject_Add(Rizin-Bundled
SOURCE_DIR "${RIZIN_SOURCE_DIR}"
CONFIGURE_COMMAND "${MESON}" "<SOURCE_DIR>" ${MESON_OPTIONS} && "${MESON}" configure ${MESON_OPTIONS} --buildtype "$<$<CONFIG:Debug>:debug>$<$<NOT:$<CONFIG:Debug>>:release>"
BUILD_COMMAND "${NINJA}"
BUILD_ALWAYS TRUE
INSTALL_COMMAND "${NINJA}" install)
set(Rizin_INCLUDE_DIRS "${RIZIN_INSTALL_DIR}/include/librz" "${RIZIN_INSTALL_DIR}/include/librz/sdb")
@ -56,14 +57,14 @@ endif()
# TODO: This version number should be fetched automatically
# instead of being hardcoded.
set (Rizin_VERSION 0.4)
set (Rizin_VERSION 0.5)
set (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_asm rz_debug
rz_hash rz_bin rz_lang rz_il rz_analysis rz_parse rz_bp rz_egg rz_reg
rz_search rz_syscall rz_socket rz_magic rz_crypto rz_type rz_diff rz_sign
rz_demangler)
set (RZ_EXTRA_LIBS rz_main)
set (RZ_BIN rz-agent rz-bin rizin rz-diff rz-find rz-gg rz-hash rz-run rz-asm rz-ax)
set (RZ_BIN rz-bin rizin rz-diff rz-find rz-gg rz-hash rz-run rz-asm rz-ax)
target_link_libraries(Rizin INTERFACE
${RZ_LIBS})

View File

@ -1,65 +0,0 @@
# - Find Breakpad
#
# Breakpad_FOUND - True if Breakpad has been found.
# Breakpad_INCLUDE_DIRS - Breakpad include directory
# Breakpad_LIBRARIES - List of libraries when using Breakpad.
set(Breakpad_LIBRARIES_VARS "")
if(WIN32)
find_path(Breakpad_INCLUDE_DIRS
client/windows/handler/exception_handler.h
HINTS
"${CMAKE_CURRENT_SOURCE_DIR}/Breakpad/src/src")
set(Breakpad_LIBRARY_NAMES
exception_handler
crash_generation_client
common
)
set(Breakpad_LIBRARIES "")
foreach(libname ${Breakpad_LIBRARY_NAMES})
find_library(Breakpad_LIBRARY_${libname}
${libname}
HINTS
"${CMAKE_CURRENT_SOURCE_DIR}/Breakpad/src/src/client/windows/Release/lib"
REQUIRED)
list(APPEND Breakpad_LIBRARIES ${Breakpad_LIBRARY_${libname}})
list(APPEND Breakpad_LIBRARIES_VARS "Breakpad_LIBRARY_${libname}")
endforeach()
set (Breakpad_LINK_LIBRARIES ${Breakpad_LIBRARIES})
set(Breakpad_LIBRARY_DIRS "")
elseif(APPLE)
find_library(Breakpad_LINK_LIBRARIES Breakpad REQUIRED)
set(Breakpad_LIBRARIES ${Breakpad_LINK_LIBRARIES})
# Assumes Breakpad is packed as Framework
set(Breakpad_INCLUDE_DIRS "${Breakpad_LINK_LIBRARIES}/Headers")
else()
set(Breakpad_CMAKE_PREFIX_PATH_TEMP ${CMAKE_PREFIX_PATH})
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Breakpad/prefix")
find_package(PkgConfig REQUIRED)
pkg_search_module(Breakpad REQUIRED breakpad-client)
# reset CMAKE_PREFIX_PATH
set(CMAKE_PREFIX_PATH ${Breakpad_CMAKE_PREFIX_PATH_TEMP})
mark_as_advanced(Breakpad_CMAKE_PREFIX_PATH_TEMP)
endif()
# could be simplified in > cmake 3.11 using pkg_search_module IMPORTED_TARGET [GLOBAL] but this would still be required for windows
add_library(Breakpad::client INTERFACE IMPORTED)
set_target_properties(Breakpad::client PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Breakpad_INCLUDE_DIRS}")
set_target_properties(Breakpad::client PROPERTIES
INTERFACE_LINK_LIBRARIES "${Breakpad_LINK_LIBRARIES}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Breakpad REQUIRED_VARS Breakpad_LIBRARIES Breakpad_INCLUDE_DIRS ${Breakpad_LIBRARIES_VARS})
mark_as_advanced(Breakpad_LIBRARIES_VARS)

8
dist/CMakeLists.txt vendored
View File

@ -77,10 +77,6 @@ if(APPLE)
set(CPACK_DMG_VOLUME_NAME "Cutter")
set(CPACK_BUNDLE_APPLE_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/Entitlements.plist")
set(CPACK_APPLE_BUNDLE_ID "re.rizin.cutter")
if (CUTTER_ENABLE_CRASH_REPORTS)
list(APPEND CPACK_BUNDLE_APPLE_CODESIGN_FILES "/Contents/Frameworks/Breakpad.framework/Versions/Current/Resources/breakpadUtilities.dylib")
endif()
find_program(MACDEPLOYQT_PATH macdeployqt HINTS "${Qt5_DIR}/../../../bin")
if(NOT MACDEPLOYQT_PATH)
@ -168,9 +164,9 @@ if(CUTTER_PACKAGE_RZ_GHIDRA)
# installed Cutter.
ExternalProject_Add(rz-ghidra
GIT_REPOSITORY https://github.com/rizinorg/rz-ghidra
GIT_TAG v0.4.0
#GIT_TAG v0.3.0
#GIT_TAG c7a50a2e7c0a95cd52b167c9ee0fa1805223f08e
#GIT_TAG dev
GIT_TAG dev
#GIT_SHALLOW ON # disable this line when using commit hash
CONFIGURE_COMMAND ""
BUILD_COMMAND ""

View File

@ -3,8 +3,6 @@ include(BundleUtilities)
set(MACDEPLOYQT_PATH "@MACDEPLOYQT_PATH@")
set(INFO_PLIST_PATH "@CPACK_BUNDLE_PLIST@")
set(ADJUST_RIZIN_LIBS "@ADJUST_RIZIN_LIBS@")
set(CUTTER_ENABLE_CRASH_REPORTS "@CUTTER_ENABLE_CRASH_REPORTS@")
set(Breakpad_LINK_LIBRARIES "@Breakpad_LINK_LIBRARIES@")
set(CUTTER_PACKAGE_DEPENDENCIES "@CUTTER_PACKAGE_DEPENDENCIES@")
set(CUTTER_ENABLE_PYTHON "@CUTTER_ENABLE_PYTHON@")
@ -82,9 +80,3 @@ foreach(_lib ${ADJUST_RIZIN_LIBS})
get_filename_component(_name "${_lib}" NAME)
file(REMOVE "${BUNDLE_PATH}/Contents/Frameworks/${_name}")
endforeach()
if (CUTTER_ENABLE_CRASH_REPORTS)
message("Copying Breakpad ${Breakpad_LINK_LIBRARIES}")
set(_breakpad_lib "Versions/A/Breakpad")
copy_resolved_framework_into_bundle("${Breakpad_LINK_LIBRARIES}/${_breakpad_lib}" "${FRAMEWORK_DIR}/Breakpad.framework/${_breakpad_lib}")
endif()

View File

@ -2,7 +2,7 @@ $arch = $args[0]
$dist = $args[1]
$py_version = (python --version).Split()[1]
$py_base = "python" + $py_version[0] + $py_version[2]
$py_base = "python" + $py_version.Split('.')[0] + $py_version.Split('.')[1]
$py_platform = If ($arch -eq "x64") {"amd64"} Else {"win32"}
$py_url = "https://www.python.org/ftp/python/${py_version}/python-${py_version}-embed-${py_platform}.zip"

View File

@ -63,6 +63,9 @@ On Debian-based Linux distributions, all of these essential packages can be inst
sudo apt install build-essential cmake meson libzip-dev zlib1g-dev qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools
.. note::
On Debian 11 (bullseye) and higher or Ubuntu 22.04 (Jammy) and higher, replace ``qt5-default`` above with ``qtbase5-dev``.
Depending on your configuration you'll might also need the following:
::
@ -113,8 +116,7 @@ If you want to use Cutter with another version of Rizin you can set ``-DCUTTER_U
.. note::
If you are interested in building Cutter with support for Python plugins,
Syntax Highlighting, Crash Reporting and more,
please look at the full list of `CMake Building Options`_.
Syntax Highlighting and more, please look at the full list of `CMake Building Options`_.
After the build process is complete, you should have the ``Cutter`` executable in the **build** dir.
@ -252,7 +254,6 @@ Note that there are some major building options available:
Cutter binary release options, not needed for most users and might not work easily outside CI environment:
* ``CUTTER_ENABLE_CRASH_REPORTS`` is used to compile Cutter with crash handling system enabled (Breakpad).
* ``CUTTER_ENABLE_DEPENDENCY_DOWNLOADS`` Enable downloading of dependencies. Setting to OFF doesn't affect any downloads done by Rizin build. This option is used for preparing Cutter binary release packges. Turned off by default.
* ``CUTTER_PACKAGE_DEPENDENCIES`` During install step include the third party dependencies. This option is used for preparing Cutter binary release packges.
@ -271,28 +272,6 @@ Or if one wants to explicitly disable an option:
cmake -B build -DCUTTER_ENABLE_PYTHON=OFF
--------------
Compiling Cutter with Breakpad Support
--------------------------------------
If you want to build Cutter with crash handling system, you will want to first prepare Breakpad.
For this, simply run one of the scripts (according to your OS) from root Cutter directory:
.. code:: sh
source scripts/prepare_breakpad_linux.sh # Linux
source scripts/prepare_breakpad_macos.sh # MacOS
scripts/prepare_breakpad.bat # Windows
Then if you are building on Linux you want to change ``PKG_CONFIG_PATH`` environment variable
so it contains ``$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig``. For this simply run
.. code:: sh
export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH"
--------------
Troubleshooting

View File

@ -1,32 +0,0 @@
Crash Handling System
=====================
Cutter uses `Breakpad <https://github.com/google/breakpad>`__ as a backend
for crash handling.
Crash Handling System is disabled by default to not interfere with developers while debugging.
To enable this system, set the ``CUTTER_ENABLE_CRASH_REPORTS`` build option.
Solution Description
--------------------
There are only 2 source files:
* ``CrashHandler.h``
* ``CrashHandler.cpp``
And the API is very simple: One function, ``initCrashHandler()``, enables the Crash Handling System if
``CUTTER_ENABLE_CRASH_REPORTS`` is true, otherwise it does nothing.
As soon as a signal is raised, ``crashHandler(int signum)`` is called with the signal's code as an argument.
This function first writes a crash dump to the operating system's temporary directory to catch core and
memory state as it was at the moment of the crash.
Then the crash dialog is shown:
.. image :: /images/crash-dialog.png
If the user chooses to create a crash dump, the prepared dump is moved to the directory specified by the user.
And then the success dialog is shown:
.. image :: /images/success-dump-dialog.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

2
rizin

@ -1 +1 @@
Subproject commit 9023f8b997db210cef3b9a25cf1748fbc94942ed
Subproject commit 12898a365e70c22892d78abdd2627e7269533c5f

View File

@ -3,4 +3,6 @@ brew "ccache"
brew "openssl"
brew "xz"
brew "llvm"
brew "meson"
brew "meson"
brew "coreutils"
brew "pkg-config"

View File

@ -1,34 +0,0 @@
{
'includes': [
'../../build/common.gypi'
],
'targets': [
{
'target_name': 'build_all',
'type': 'none',
'dependencies': [
'./crash_generation/crash_generation.gyp:*',
'./handler/exception_handler.gyp:*',
'./sender/crash_report_sender.gyp:*',
]
},
{
'target_name': 'common',
'type': 'static_library',
'include_dirs': [
'<(DEPTH)',
],
'direct_dependent_settings': {
'include_dirs': [
'<(DEPTH)',
]
},
'sources': [
'<(DEPTH)/common/windows/guid_string.cc',
'<(DEPTH)/common/windows/guid_string.h',
'<(DEPTH)/common/windows/http_upload.h',
'<(DEPTH)/common/windows/string_utils.cc',
]
}
]
}

View File

@ -1,55 +0,0 @@
import sys
import os
import subprocess
import re
import atexit
if len(sys.argv) != 3:
print(f"usage: {sys.argv[0]} [Cutter.AppImage] [symbols dir]")
exit(1)
root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
def store_syms(syms, syms_dir):
m = re.match(b"MODULE ([^ ]+) ([^ ]+) ([^ ]+) (.+)\n.*", syms)
if m is None:
print("Invalid dump_syms output")
return
(modos, modarch, modhash, modname) = m.groups()
modname = modname.decode("utf-8")
modhash = modhash.decode("utf-8")
symdir = os.path.join(syms_dir, modname, modhash)
symfile = f"{modname}.sym"
os.makedirs(symdir)
symfile_path = os.path.join(symdir, symfile)
with open(symfile_path, "wb") as f:
f.write(syms)
print(symfile_path)
def dump_syms(binary, syms_dir):
dump_syms_exec = os.path.join(root, "breakpad/src/tools/linux/dump_syms/dump_syms")
syms = subprocess.run([dump_syms_exec, binary], capture_output=True).stdout
store_syms(syms, syms_dir)
appimage = sys.argv[1]
syms_dst = sys.argv[2]
# stdbuf workaround is needed before https://github.com/AppImage/AppImageKit/commit/e827baa719f5444aeef7202fe1f71c97d4200dde
appimage_p = subprocess.Popen(["stdbuf", "-oL", appimage, "--appimage-mount"], stdout=subprocess.PIPE)
def kill_appimage():
appimage_p.kill()
atexit.register(kill_appimage)
mount_dir = appimage_p.stdout.readline().strip().decode("utf-8")
binaries = [ os.path.join(mount_dir, "usr/bin/cutter") ]
for f in os.scandir(os.path.join(mount_dir, "usr/lib")):
if f.is_dir() or f.is_symlink():
continue
binaries.append(f.path)
for b in binaries:
dump_syms(b, syms_dst)

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +0,0 @@
@ECHO OFF
SET ROOT_DIR=%CD%
powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget https://storage.googleapis.com/chrome-infra/depot_tools.zip -OutFile depot_tools.zip "
7z -bd x %ROOT_DIR%\depot_tools.zip -odepot_tools
powershell -Command "depot_tools\update_depot_tools"
SET BUFF_PATH=%PATH%
SET DEPOT_TOOLS=%ROOT_DIR%\depot_tools
set PATH=%DEPOT_TOOLS%;%BUFF_PATH%
mkdir %ROOT_DIR%\src\breakpad
CD %ROOT_DIR%\src\breakpad
powershell -Command "fetch breakpad"
powershell -Command "gclient sync"
CD %ROOT_DIR%\src\breakpad\src
powershell -Command "git reset --hard 756daa536ad819eff80172aaab262fb71d1e89fd"
CD %ROOT_DIR%\src\breakpad\src\src\client\windows
DEL %CD%\breakpad_client.gyp
DEL %CD%\breakpad_client.sln
DEL %CD%\common.vcxproj
DEL %CD%\common.vcxproj.filters
DEL %CD%\build_all.vcxproj
COPY %ROOT_DIR%\scripts\breakpad_client.gyp %CD%
CD %ROOT_DIR%\src\breakpad\src\src
SET GYP_MSVS_VERSION=2017
powershell -Command "tools\gyp\gyp.bat --no-circular-check client\windows\breakpad_client.gyp -Dwin_release_RuntimeLibrary=2 -Dwin_debug_RuntimeLibrary=2 -Dplatform=%ARCH% -Dconfiguration=release"
devenv client\windows\breakpad_client.sln /upgrade
set PATH=%BUFF_PATH%
msbuild /m %CD%\client\windows\breakpad_client.sln /p:Configuration=release /p:Platform=%ARCH% || exit /b 1
CD %ROOT_DIR%

View File

@ -1,11 +0,0 @@
#!/bin/sh
set -euo pipefail
git clone https://github.com/google/breakpad.git
cd breakpad
git clone https://chromium.googlesource.com/linux-syscall-support src/third_party/lss
CFLAGS=-w CXXFLAGS=-w ./configure --disable-tools --prefix=`pwd`/prefix && make -j4 && make install || exit 1
export CUSTOM_BREAKPAD_PREFIX="`pwd`/prefix"
cd ..

View File

@ -1,24 +0,0 @@
#!/bin/sh
set -euo pipefail
SCRIPTPATH=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
DIR="$SCRIPTPATH/.."
cd "$DIR"
BREAKPAD_FRAMEWORK_DIR="$DIR/breakpad/framework"
BREAKPAD_DUMP_SYMS_DIR="$DIR/breakpad/bin"
git clone https://github.com/google/breakpad.git
mkdir $BREAKPAD_FRAMEWORK_DIR
mkdir $BREAKPAD_DUMP_SYMS_DIR
cd breakpad
git checkout 4d550cceca107f36c4bc1ea1126b7d32cc50f424
git apply "$SCRIPTPATH/breakpad_macos.patch"
cd src/client/mac/ && xcodebuild -sdk macosx MACOSX_DEPLOYMENT_TARGET=10.14
cp -R build/Release/Breakpad.framework "$BREAKPAD_FRAMEWORK_DIR"
cd $DIR/breakpad
cp -R src/. framework/Breakpad.framework/Headers
export BREAKPAD_FRAMEWORK_DIR=$BREAKPAD_FRAMEWORK_DIR
cd $DIR

View File

@ -5,6 +5,7 @@ set(SOURCES
Main.cpp
core/Cutter.cpp
core/CutterJson.cpp
core/RizinCpp.cpp
dialogs/EditStringDialog.cpp
dialogs/WriteCommandsDialogs.cpp
widgets/DisassemblerGraphView.cpp
@ -72,7 +73,6 @@ set(SOURCES
widgets/CutterTreeWidget.cpp
widgets/GraphWidget.cpp
widgets/OverviewWidget.cpp
common/JsonTreeItem.cpp
common/JsonModel.cpp
dialogs/VersionInfoDialog.cpp
widgets/FlirtWidget.cpp
@ -155,6 +155,7 @@ set(HEADER_FILES
core/CutterCommon.h
core/CutterDescriptions.h
core/CutterJson.h
core/RizinCpp.h
dialogs/EditStringDialog.h
dialogs/WriteCommandsDialogs.h
widgets/DisassemblerGraphView.h
@ -222,7 +223,6 @@ set(HEADER_FILES
widgets/CutterTreeWidget.h
widgets/GraphWidget.h
widgets/OverviewWidget.h
common/JsonTreeItem.h
common/JsonModel.h
dialogs/VersionInfoDialog.h
widgets/FlirtWidget.h
@ -262,7 +262,6 @@ set(HEADER_FILES
common/RunScriptTask.h
common/Json.h
dialogs/EditMethodDialog.h
common/CrashHandler.h
dialogs/TypesInteractionDialog.h
widgets/SdbWidget.h
plugins/PluginManager.h
@ -393,10 +392,6 @@ if (CUTTER_ENABLE_PYTHON)
list(APPEND HEADER_FILES common/QtResImporter.h common/PythonManager.h common/PythonAPI.h)
endif()
if(CUTTER_ENABLE_CRASH_REPORTS)
list(APPEND SOURCES common/CrashHandler.cpp)
endif()
if(CUTTER_ENABLE_PYTHON_BINDINGS)
set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bindings")
set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/bindings")
@ -442,8 +437,11 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
set_source_files_properties(${BINDINGS_SOURCE} PROPERTIES COMPILE_FLAGS -w)
endif()
# Make a source group for Visual Studio
set(CUTTER_SOURCES ${OPTIONS} ${UI_FILES} ${QRC_FILES} ${PLATFORM_RESOURCES} ${SOURCES} ${HEADER_FILES})
source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" FILES ${CUTTER_SOURCES})
add_executable(Cutter ${OPTIONS} ${UI_FILES} ${QRC_FILES} ${PLATFORM_RESOURCES} ${SOURCES} ${HEADER_FILES} ${BINDINGS_SOURCE})
add_executable(Cutter ${CUTTER_SOURCES} ${BINDINGS_SOURCE})
set_target_properties(Cutter PROPERTIES
OUTPUT_NAME cutter
RUNTIME_OUTPUT_DIRECTORY ..
@ -452,6 +450,9 @@ set_target_properties(Cutter PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(Cutter PRIVATE CUTTER_SOURCE_BUILD)
# Set Cutter as the startup project in Visual Studio
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Cutter)
set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .)
foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES})
target_include_directories(Cutter PUBLIC
@ -465,19 +466,6 @@ if (TARGET Graphviz::GVC)
target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_GRAPHVIZ)
endif()
if(CUTTER_ENABLE_CRASH_REPORTS)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(Cutter PRIVATE Threads::Threads)
add_definitions(-DCUTTER_ENABLE_CRASH_REPORTS)
if (NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g ")
endif()
find_package(Breakpad REQUIRED)
target_link_libraries(Cutter PRIVATE Breakpad::client)
endif()
target_link_libraries(Cutter PUBLIC ${QT_PREFIX}::Core ${QT_PREFIX}::Widgets ${QT_PREFIX}::Gui PRIVATE ${QT_PREFIX}::Svg ${QT_PREFIX}::Network)
if (CUTTER_QT6)
target_link_libraries(Cutter PUBLIC Qt6::Core5Compat Qt6::SvgWidgets)

View File

@ -1,5 +1,4 @@
#include "common/PythonManager.h"
#include "common/CrashHandler.h"
#include "CutterApplication.h"
#include "plugins/PluginManager.h"
#include "CutterConfig.h"

View File

@ -3,7 +3,6 @@
#include "core/MainWindow.h"
#include "common/UpdateWorker.h"
#include "CutterConfig.h"
#include "common/CrashHandler.h"
#include "common/SettingsUpgrade.h"
#include <QJsonObject>
@ -55,17 +54,6 @@ static void connectToConsole()
int main(int argc, char *argv[])
{
#ifdef CUTTER_ENABLE_CRASH_REPORTS
if (argc >= 3 && QString::fromLocal8Bit(argv[1]) == "--start-crash-handler") {
QApplication app(argc, argv);
QString dumpLocation = QString::fromLocal8Bit(argv[2]);
showCrashDialog(dumpLocation);
return 0;
}
initCrashHandler();
#endif
#ifdef Q_OS_WIN
connectToConsole();
#endif
@ -74,6 +62,9 @@ int main(int argc, char *argv[])
qRegisterMetaType<QList<FunctionDescription>>();
QCoreApplication::setOrganizationName("rizin");
#ifndef Q_OS_MACOS // don't set on macOS so that it doesn't affect config path there
QCoreApplication::setOrganizationDomain("rizin.re");
#endif
QCoreApplication::setApplicationName("cutter");
// Importing settings after setting rename, needs separate handling in addition to regular version to version upgrade.

View File

@ -8,26 +8,18 @@
void openIssue()
{
RzCoreLocked core(Core());
RzBinFile *bf = rz_bin_cur(core->bin);
RzBinInfo *info = rz_bin_get_info(core->bin);
RzBinPlugin *plugin = rz_bin_file_cur_plugin(bf);
QString url, osInfo, format, arch, type;
// Pull in info needed for git issue
osInfo = QSysInfo::productType() + " "
+ (QSysInfo::productVersion() == "unknown" ? "" : QSysInfo::productVersion());
CutterJson docu = Core()->getFileInfo();
CutterJson coreObj = docu["core"];
CutterJson binObj = docu["bin"];
if (binObj.size()) {
format = coreObj["format"].toString();
arch = binObj["arch"].toString();
if (binObj["type"].valid()) {
type = coreObj["type"].toString();
} else {
type = "N/A";
}
} else {
format = coreObj["format"].toString();
arch = "N/A";
type = "N/A";
}
format = plugin && RZ_STR_ISNOTEMPTY(plugin->name) ? plugin->name : "N/A";
arch = info && RZ_STR_ISNOTEMPTY(info->arch) ? info->arch : "N/A";
type = info && RZ_STR_ISNOTEMPTY(info->type) ? info->type : "N/A";
url = "https://github.com/rizinorg/cutter/issues/new?&body=**Environment information**\n* "
"Operating System: "
+ osInfo + "\n* Cutter version: " + CUTTER_VERSION_FULL + "\n* Obtained from:\n"

View File

@ -201,7 +201,6 @@ void Configuration::resetAll()
{
// Don't reset all rizin vars, that currently breaks a bunch of stuff.
// settingsFile.remove()+loadInitials() should reset all settings configurable using Cutter GUI.
// Core()->cmdRaw("e-");
Core()->setSettings();
// Delete the file so no extra configuration is in it.

View File

@ -1,163 +0,0 @@
#include "CrashHandler.h"
#include "BugReporting.h"
#include <QMessageBox>
#include <QPushButton>
#include <QFileDialog>
#include <QStandardPaths>
#include <QTime>
#include <QApplication>
#include <QString>
#include <QFile>
#include <QDir>
#include <QMap>
#include <QProcess>
#if defined(Q_OS_LINUX)
# include "client/linux/handler/exception_handler.h"
#elif defined(Q_OS_WIN32)
# include "client/windows/handler/exception_handler.h"
#elif defined(Q_OS_MACOS)
# include "client/mac/handler/exception_handler.h"
#endif // Q_OS
static google_breakpad::ExceptionHandler *exceptionHandler = nullptr;
static void finishCrashHandler()
{
delete exceptionHandler;
}
#ifdef Q_OS_WIN32
// Called if crash dump was successfully created
// Saves path to file
bool callback(const wchar_t *_dump_dir, const wchar_t *_minidump_id, void *context,
EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion, bool success)
{
const QDir dir = QString::fromWCharArray(_dump_dir);
const QString id = QString::fromWCharArray(_minidump_id);
QProcess::startDetached(QCoreApplication::applicationFilePath(),
{ "--start-crash-handler", dir.filePath(id + ".dmp") });
_exit(1);
return true;
}
#elif defined(Q_OS_LINUX)
// Called if crash dump was successfully created
// Saves path to file
bool callback(const google_breakpad::MinidumpDescriptor &md, void *context, bool b)
{
QProcess::startDetached(QCoreApplication::applicationFilePath(),
{ "--start-crash-handler", md.path() });
_exit(1);
return true;
}
#elif defined(Q_OS_MACOS)
// Called if crash dump was successfully created
// Saves path to file
bool callback(const char *dump_dir, const char *minidump_id, void *context, bool succeeded)
{
const QDir dir = QString::fromUtf8(dump_dir);
const QString id = QString::fromUtf8(minidump_id);
QProcess::startDetached(QCoreApplication::applicationFilePath(),
{ "--start-crash-handler", dir.filePath(id + ".dmp") });
_exit(1);
return true;
}
#endif // Q_OS
void initCrashHandler()
{
if (exceptionHandler) {
return;
}
// Here will be placed crash dump at the first place
// and then moved if needed
#if defined(Q_OS_LINUX)
static std::string tmpLocation =
QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString();
exceptionHandler = new google_breakpad::ExceptionHandler(
google_breakpad::MinidumpDescriptor(tmpLocation), nullptr, callback, nullptr, true, -1);
#elif defined(Q_OS_MACOS)
static std::string tmpLocation =
QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdString();
exceptionHandler = new google_breakpad::ExceptionHandler(tmpLocation, nullptr, callback,
nullptr, true, nullptr);
#else
static std::wstring tmpLocation =
QStandardPaths::writableLocation(QStandardPaths::TempLocation).toStdWString();
exceptionHandler =
new google_breakpad::ExceptionHandler(tmpLocation, nullptr, callback, nullptr,
google_breakpad::ExceptionHandler::HANDLER_ALL);
#endif
atexit(finishCrashHandler);
}
void showCrashDialog(const QString &dumpFile)
{
QMessageBox mb;
mb.setWindowTitle(QObject::tr("Crash"));
mb.setText(QObject::tr("Cutter received a signal it can't handle and will close.<br/>"
"Would you like to create a crash dump for a bug report?"));
mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
mb.button(QMessageBox::Yes)->setText(QObject::tr("Create a Crash Dump"));
mb.button(QMessageBox::No)->setText(QObject::tr("Quit"));
mb.setDefaultButton(QMessageBox::Yes);
bool ok = false;
int ret = mb.exec();
if (ret == QMessageBox::Yes) {
QString dumpSaveFileName;
int placementFailCounter = 0;
do {
placementFailCounter++;
if (placementFailCounter == 4) {
break;
}
dumpSaveFileName = QFileDialog::getSaveFileName(
nullptr, QObject::tr("Choose a directory to save the crash dump in"),
QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
+ QDir::separator() + "Cutter_crash_dump_"
+ QDate::currentDate().toString("dd.MM.yy") + "_"
+ QTime::currentTime().toString("HH.mm.ss") + ".dmp",
QObject::tr("Minidump (*.dmp)"));
if (dumpSaveFileName.isEmpty()) {
return;
}
if (QFile::rename(dumpFile, dumpSaveFileName)) {
ok = true;
break;
}
QMessageBox::critical(nullptr, QObject::tr("Save Crash Dump"),
QObject::tr("Failed to write to %1.<br/>"
"Please make sure you have access to that directory "
"and try again.")
.arg(QFileInfo(dumpSaveFileName).dir().path()));
} while (true);
if (ok) {
QMessageBox info;
info.setWindowTitle(QObject::tr("Success"));
info.setText(QObject::tr("<a href=\"%1\">Crash dump</a> was successfully created.")
.arg(QFileInfo(dumpSaveFileName).dir().path()));
info.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
info.button(QMessageBox::Yes)->setText(QObject::tr("Open an Issue"));
info.button(QMessageBox::No)->setText(QObject::tr("Quit"));
info.setDefaultButton(QMessageBox::Yes);
int ret = info.exec();
if (ret == QMessageBox::Yes) {
openIssue();
}
} else {
QMessageBox::critical(nullptr, QObject::tr("Error"),
QObject::tr("Error occurred during crash dump creation."));
}
} else {
QFile f(dumpFile);
f.remove();
}
}

View File

@ -1,16 +0,0 @@
#ifndef CRASH_HANDLER_H
#define CRASH_HANDLER_H
#include <QString>
/**
* @fn void initCrashHandler()
*
* If CUTTER_ENABLE_CRASH_REPORTS is true, initializes
* crash handling and reporting, otherwise does nothing.
*/
void initCrashHandler();
void showCrashDialog(const QString &dumpFile);
#endif // CRASH_HANDLER_H

View File

@ -49,8 +49,7 @@ void DecompilerHighlighter::highlightBlock(const QString &)
size_t start = block.position();
size_t end = block.position() + block.length();
std::unique_ptr<RzPVector, decltype(&rz_pvector_free)> annotations(
rz_annotated_code_annotations_range(code, start, end), &rz_pvector_free);
auto annotations = fromOwned(rz_annotated_code_annotations_range(code, start, end));
void **iter;
rz_pvector_foreach(annotations.get(), iter)
{

View File

@ -9,15 +9,6 @@ Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)
core = Core();
keywordFormat.setForeground(QColor(65, 131, 215));
for (const QString &pattern : this->core->opcodes) {
rule.pattern.setPattern("\\b" + pattern + "\\b");
rule.pattern.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
rule.format = keywordFormat;
highlightingRules.append(rule);
}
regFormat.setForeground(QColor(236, 100, 75));
for (const QString &pattern : this->core->regs) {

View File

@ -1,123 +1,38 @@
#include "JsonModel.h"
#include <QIODevice>
JsonModel::JsonModel(QObject *parent) : QAbstractItemModel(parent)
QTreeWidgetItem *Cutter::jsonTreeWidgetItem(const QString &key, const CutterJson &json)
{
mRootItem = new JsonTreeItem;
mHeaders.append("key");
mHeaders.append("value");
}
JsonModel::~JsonModel()
{
delete mRootItem;
}
bool JsonModel::load(QIODevice *device)
{
return loadJson(device->readAll());
}
bool JsonModel::loadJson(const QByteArray &json)
{
mDocument = QJsonDocument::fromJson(json);
if (!mDocument.isNull()) {
beginResetModel();
delete mRootItem;
if (mDocument.isArray()) {
mRootItem = JsonTreeItem::load(QJsonValue(mDocument.array()));
} else {
mRootItem = JsonTreeItem::load(QJsonValue(mDocument.object()));
QString val;
switch (json.type()) {
case RZ_JSON_STRING:
val = json.toString();
break;
case RZ_JSON_BOOLEAN:
val = json.toBool() ? "true" : "false";
break;
case RZ_JSON_DOUBLE:
val = QString::number(json.lowLevelValue()->num.dbl_value);
break;
case RZ_JSON_INTEGER:
val = QString::number(json.toUt64());
break;
case RZ_JSON_NULL:
val = "null";
break;
case RZ_JSON_OBJECT:
case RZ_JSON_ARRAY:
break;
}
auto r = new QTreeWidgetItem(QStringList({ key, val }));
if (json.type() == RZ_JSON_ARRAY) {
size_t i = 0;
for (const auto &child : json) {
r->addChild(jsonTreeWidgetItem(QString::number(i++), child));
}
} else if (json.type() == RZ_JSON_OBJECT) {
for (const auto &child : json) {
r->addChild(jsonTreeWidgetItem(child.key(), child));
}
endResetModel();
return true;
}
return false;
}
QVariant JsonModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
JsonTreeItem *item = static_cast<JsonTreeItem *>(index.internalPointer());
if (role == Qt::DisplayRole) {
if (index.column() == 0)
return QString("%1").arg(item->key());
if (index.column() == 1)
return QString("%1").arg(item->value());
}
return QVariant();
}
QVariant JsonModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
return mHeaders.value(section);
} else
return QVariant();
}
QModelIndex JsonModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
JsonTreeItem *parentItem;
if (!parent.isValid())
parentItem = mRootItem;
else
parentItem = static_cast<JsonTreeItem *>(parent.internalPointer());
JsonTreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex JsonModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
JsonTreeItem *childItem = static_cast<JsonTreeItem *>(index.internalPointer());
JsonTreeItem *parentItem = childItem->parent();
if (parentItem == mRootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int JsonModel::rowCount(const QModelIndex &parent) const
{
JsonTreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = mRootItem;
else
parentItem = static_cast<JsonTreeItem *>(parent.internalPointer());
return parentItem->childCount();
}
int JsonModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 2;
return r;
}

View File

@ -2,37 +2,13 @@
#ifndef JSONMODEL_H
#define JSONMODEL_H
#include <QAbstractItemModel>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QIcon>
#include <QTreeWidgetItem>
#include "CutterJson.h"
#include "JsonTreeItem.h"
namespace Cutter {
class JsonTreeItem;
QTreeWidgetItem *jsonTreeWidgetItem(const QString &key, const CutterJson &json);
class JsonModel : public QAbstractItemModel
{
public:
explicit JsonModel(QObject *parent = nullptr);
bool load(QIODevice *device);
bool loadJson(const QByteArray &json);
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
~JsonModel();
private:
JsonTreeItem *mRootItem;
QJsonDocument mDocument;
QStringList mHeaders;
};
#endif // JSONMODEL_H

View File

@ -1,104 +0,0 @@
#include "JsonTreeItem.h"
JsonTreeItem::JsonTreeItem(JsonTreeItem *parent)
{
mParent = parent;
}
JsonTreeItem::~JsonTreeItem()
{
qDeleteAll(mChilds);
}
void JsonTreeItem::appendChild(JsonTreeItem *item)
{
mChilds.append(item);
}
JsonTreeItem *JsonTreeItem::child(int row)
{
return mChilds.value(row);
}
JsonTreeItem *JsonTreeItem::parent()
{
return mParent;
}
int JsonTreeItem::childCount() const
{
return mChilds.count();
}
int JsonTreeItem::row() const
{
if (mParent)
return mParent->mChilds.indexOf(const_cast<JsonTreeItem *>(this));
return 0;
}
void JsonTreeItem::setKey(const QString &key)
{
mKey = key;
}
void JsonTreeItem::setValue(const QString &value)
{
mValue = value;
}
void JsonTreeItem::setType(const QJsonValue::Type &type)
{
mType = type;
}
QString JsonTreeItem::key() const
{
return mKey;
}
QString JsonTreeItem::value() const
{
return mValue;
}
QJsonValue::Type JsonTreeItem::type() const
{
return mType;
}
JsonTreeItem *JsonTreeItem::load(const QJsonValue &value, JsonTreeItem *parent)
{
JsonTreeItem *rootItem = new JsonTreeItem(parent);
rootItem->setKey("root");
if (value.isObject()) {
// Get all QJsonValue childs
for (const QString &key : value.toObject().keys()) {
QJsonValue v = value.toObject().value(key);
JsonTreeItem *child = load(v, rootItem);
child->setKey(key);
child->setType(v.type());
rootItem->appendChild(child);
}
} else if (value.isArray()) {
// Get all QJsonValue childs
int index = 0;
for (const QJsonValue &v : value.toArray()) {
JsonTreeItem *child = load(v, rootItem);
child->setKey(QString::number(index));
child->setType(v.type());
rootItem->appendChild(child);
++index;
}
} else {
rootItem->setValue(value.toVariant().toString());
rootItem->setType(value.type());
}
return rootItem;
}

View File

@ -1,39 +0,0 @@
#ifndef JSONTREEITEM_H
#define JSONTREEITEM_H
#include <QAbstractItemModel>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QIcon>
#include "JsonModel.h"
class JsonTreeItem
{
public:
JsonTreeItem(JsonTreeItem *parent = nullptr);
~JsonTreeItem();
void appendChild(JsonTreeItem *item);
JsonTreeItem *child(int row);
JsonTreeItem *parent();
int childCount() const;
int row() const;
void setKey(const QString &key);
void setValue(const QString &value);
void setType(const QJsonValue::Type &type);
QString key() const;
QString value() const;
QJsonValue::Type type() const;
static JsonTreeItem *load(const QJsonValue &value, JsonTreeItem *parent = nullptr);
private:
QString mKey;
QString mValue;
QJsonValue::Type mType;
QList<JsonTreeItem *> mChilds;
JsonTreeItem *mParent;
};
#endif // JSONTREEITEM_H

View File

@ -16,7 +16,10 @@ void RunScriptTask::runTask()
{
if (!this->fileName.isNull()) {
log(tr("Executing script..."));
Core()->cmdTask(". " + this->fileName);
Core()->functionTask([&](RzCore *core) {
rz_core_run_script(core, this->fileName.toUtf8().constData());
return nullptr;
});
if (isInterrupted()) {
return;
}

View File

@ -19,7 +19,6 @@
* {
* TempConfig tempConfig;
* tempConfig.set("asm.arch", "x86").set("asm.comments", false);
* return Core()->cmdRaw("pd");
* // config automatically restored at the end of scope
* }
* \endcode

View File

@ -120,13 +120,6 @@ static void updateOwnedCharPtr(char *&variable, const QString &newValue)
variable = strdup(data.data());
}
static QString fromOwnedCharPtr(char *str)
{
QString result(str ? str : "");
rz_mem_free(str);
return result;
}
static bool reg_sync(RzCore *core, RzRegisterType type, bool write)
{
if (rz_core_is_debug(core)) {
@ -377,6 +370,37 @@ QString CutterCore::cmd(const char *str)
return o;
}
QString CutterCore::getFunctionExecOut(const std::function<bool(RzCore *)> &fcn, const RVA addr)
{
CORE_LOCK();
RVA offset = core->offset;
seekSilent(addr);
QString o = {};
rz_cons_push();
bool is_pipe = core->is_pipe;
core->is_pipe = true;
if (!fcn(core)) {
core->is_pipe = is_pipe;
rz_cons_pop();
goto clean_return;
}
core->is_pipe = is_pipe;
rz_cons_filter();
o = rz_cons_get_buffer();
rz_cons_pop();
rz_cons_echo(NULL);
clean_return:
if (offset != core->offset) {
seekSilent(offset);
}
return o;
}
bool CutterCore::isRedirectableDebugee()
{
if (!currentlyDebugging || currentlyAttachedToPID != -1) {
@ -406,48 +430,6 @@ bool CutterCore::isDebugTaskInProgress()
return false;
}
bool CutterCore::asyncCmdEsil(const char *command, QSharedPointer<RizinTask> &task)
{
asyncCmd(command, task);
if (task.isNull()) {
return false;
}
connect(task.data(), &RizinCmdTask::finished, task.data(), [this, task]() {
QString res = qobject_cast<RizinCmdTask *>(task.data())->getResult();
if (res.contains(QStringLiteral("[ESIL] Stopped execution in an invalid instruction"))) {
msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can "
"disable this in Preferences");
}
});
return true;
}
bool CutterCore::asyncCmd(const char *str, QSharedPointer<RizinTask> &task)
{
if (!task.isNull()) {
return false;
}
CORE_LOCK();
RVA offset = core->offset;
task = QSharedPointer<RizinTask>(new RizinCmdTask(str, true));
connect(task.data(), &RizinTask::finished, task.data(), [this, offset, task]() {
CORE_LOCK();
if (offset != core->offset) {
updateSeek();
}
});
return true;
}
bool CutterCore::asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<RizinTask> &task)
{
if (!task.isNull()) {
@ -468,6 +450,13 @@ bool CutterCore::asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<R
return true;
}
void CutterCore::functionTask(std::function<void *(RzCore *)> fcn)
{
auto task = std::unique_ptr<RizinTask>(new RizinFunctionTask(std::move(fcn), true));
task->startTask();
task->joinTask();
}
QString CutterCore::cmdRawAt(const char *cmd, RVA address)
{
QString res;
@ -486,8 +475,8 @@ QString CutterCore::cmdRaw(const char *cmd)
CORE_LOCK();
rz_cons_push();
// rz_cmd_call does not return the output of the command
rz_cmd_call(core->rcmd, cmd);
// rz_core_cmd does not return the output of the command
rz_core_cmd(core, cmd, 0);
// we grab the output straight from rz_cons
res = rz_cons_get_buffer();
@ -510,18 +499,6 @@ CutterJson CutterCore::cmdj(const char *str)
return parseJson(res, str);
}
CutterJson CutterCore::cmdjAt(const char *str, RVA address)
{
CutterJson res;
RVA oldOffset = getOffset();
seekSilent(address);
res = cmdj(str);
seekSilent(oldOffset);
return res;
}
QString CutterCore::cmdTask(const QString &str)
{
RizinCmdTask task(str);
@ -530,14 +507,6 @@ QString CutterCore::cmdTask(const QString &str)
return task.getResult();
}
CutterJson CutterCore::cmdjTask(const QString &str)
{
RizinCmdTask task(str);
task.startTask();
task.joinTask();
return task.getResultJson();
}
CutterJson CutterCore::parseJson(char *res, const char *cmd)
{
if (!res) {
@ -687,7 +656,7 @@ bool CutterCore::mapFile(QString path, RVA mapaddr)
{
CORE_LOCK();
RVA addr = mapaddr != RVA_INVALID ? mapaddr : 0;
ut64 baddr = Core()->getFileInfo()["bin"]["baddr"].toUt64();
ut64 baddr = rz_bin_get_baddr(core->bin);
if (rz_core_file_open(core, path.toUtf8().constData(), RZ_PERM_RX, addr)) {
rz_core_bin_load(core, path.toUtf8().constData(), baddr);
} else {
@ -746,34 +715,31 @@ void CutterCore::delFlag(const QString &name)
emit flagsChanged();
}
PRzAnalysisBytes CutterCore::getRzAnalysisBytesSingle(RVA addr)
{
CORE_LOCK();
ut8 buf[128];
rz_io_read_at(core->io, addr, buf, sizeof(buf));
auto seek = seekTemp(addr);
auto vec = fromOwned(rz_core_analysis_bytes(core, buf, sizeof(buf), 1));
auto ab = vec && rz_pvector_len(vec.get()) > 0
? reinterpret_cast<RzAnalysisBytes *>(rz_pvector_pop_front(vec.get()))
: nullptr;
return { ab, rz_analysis_bytes_free };
}
QString CutterCore::getInstructionBytes(RVA addr)
{
auto ret = (char *)Core()->returnAtSeek(
[&]() {
CORE_LOCK();
RzPVector *vec = rz_core_analysis_bytes(core, core->block, (int)core->blocksize, 1);
auto *ab = static_cast<RzAnalysisBytes *>(rz_pvector_head(vec));
char *str = strdup(ab->bytes);
rz_pvector_free(vec);
return str;
},
addr);
return fromOwnedCharPtr(ret);
auto ab = getRzAnalysisBytesSingle(addr);
return ab ? ab->bytes : "";
}
QString CutterCore::getInstructionOpcode(RVA addr)
{
auto ret = (char *)Core()->returnAtSeek(
[&]() {
CORE_LOCK();
RzPVector *vec = rz_core_analysis_bytes(core, core->block, (int)core->blocksize, 1);
auto *ab = static_cast<RzAnalysisBytes *>(rz_pvector_head(vec));
char *str = strdup(ab->opcode);
rz_pvector_free(vec);
return str;
},
addr);
return fromOwnedCharPtr(ret);
auto ab = getRzAnalysisBytesSingle(addr);
return ab ? ab->opcode : "";
}
void CutterCore::editInstruction(RVA addr, const QString &inst, bool fillWithNops)
@ -790,14 +756,20 @@ void CutterCore::editInstruction(RVA addr, const QString &inst, bool fillWithNop
void CutterCore::nopInstruction(RVA addr)
{
CORE_LOCK();
applyAtSeek([&]() { rz_core_hack(core, "nop"); }, addr);
{
auto seek = seekTemp(addr);
rz_core_hack(core, "nop");
}
emit instructionChanged(addr);
}
void CutterCore::jmpReverse(RVA addr)
{
CORE_LOCK();
applyAtSeek([&]() { rz_core_hack(core, "recj"); }, addr);
{
auto seek = seekTemp(addr);
rz_core_hack(core, "recj");
}
emit instructionChanged(addr);
}
@ -879,8 +851,8 @@ QString CutterCore::getString(RVA addr, uint64_t len, RzStrEnc encoding, bool es
opt.length = len;
opt.encoding = encoding;
opt.escape_nl = escape_nl;
char *s = (char *)returnAtSeek([&]() { return rz_str_stringify_raw_buffer(&opt, NULL); }, addr);
return fromOwnedCharPtr(s);
auto seek = seekTemp(addr);
return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, NULL));
}
QString CutterCore::getMetaString(RVA addr)
@ -964,12 +936,11 @@ void CutterCore::applyStructureOffset(const QString &structureOffset, RVA offset
offset = getOffset();
}
applyAtSeek(
[&]() {
CORE_LOCK();
rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData());
},
offset);
{
CORE_LOCK();
auto seek = seekTemp(offset);
rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData());
}
emit instructionChanged(offset);
}
@ -1055,24 +1026,20 @@ RVA CutterCore::prevOpAddr(RVA startAddr, int count)
RVA CutterCore::nextOpAddr(RVA startAddr, int count)
{
CORE_LOCK();
auto seek = seekTemp(startAddr);
auto vec =
fromOwned(rz_core_analysis_bytes(core, core->block, (int)core->blocksize, count + 1));
CutterJson array =
Core()->cmdj("pdj " + QString::number(count + 1) + " @ " + QString::number(startAddr));
if (!array.size()) {
return startAddr + 1;
RVA addr = startAddr + 1;
if (!vec) {
return addr;
}
CutterJson instValue = array.last();
if (instValue.type() != RZ_JSON_OBJECT) {
return startAddr + 1;
auto ab = reinterpret_cast<RzAnalysisBytes *>(rz_pvector_tail(vec.get()));
if (!(ab && ab->op)) {
return addr;
}
RVA offset = instValue[RJsonKey::offset].toRVA();
if (offset == RVA_INVALID) {
return startAddr + 1;
}
return offset;
addr = ab->op->addr;
return addr;
}
RVA CutterCore::getOffset()
@ -1296,7 +1263,8 @@ QString CutterCore::disassemble(const QByteArray &data)
QString CutterCore::disassembleSingleInstruction(RVA addr)
{
return cmdRawAt("pi 1", addr).simplified();
auto ab = getRzAnalysisBytesSingle(addr);
return QString(ab->disasm).simplified();
}
RzAnalysisFunction *CutterCore::functionIn(ut64 addr)
@ -1370,16 +1338,6 @@ QString CutterCore::flagAt(RVA addr)
return core->flags->realnames && f->realname ? f->realname : f->name;
}
void CutterCore::cmdEsil(const char *command)
{
// use cmd and not cmdRaw because of unexpected commands
QString res = cmd(command);
if (res.contains(QStringLiteral("[ESIL] Stopped execution in an invalid instruction"))) {
msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can disable "
"this in Preferences");
}
}
void CutterCore::createFunctionAt(RVA addr)
{
createFunctionAt(addr, "");
@ -1400,19 +1358,8 @@ void CutterCore::createFunctionAt(RVA addr, QString name)
RVA CutterCore::getOffsetJump(RVA addr)
{
auto rva = (RVA *)Core()->returnAtSeek(
[&]() {
CORE_LOCK();
RzPVector *vec = rz_core_analysis_bytes(core, core->block, (int)core->blocksize, 1);
auto *ab = static_cast<RzAnalysisBytes *>(rz_pvector_head(vec));
RVA *rva = new RVA(ab->op->jump);
rz_pvector_free(vec);
return rva;
},
addr);
RVA ret = *rva;
delete rva;
return ret;
auto ab = getRzAnalysisBytesSingle(addr);
return ab && ab->op ? ab->op->jump : RVA_INVALID;
}
QList<Decompiler *> CutterCore::getDecompilers()
@ -1440,17 +1387,7 @@ bool CutterCore::registerDecompiler(Decompiler *decompiler)
return true;
}
CutterJson CutterCore::getFileInfo()
{
return cmdj("ij");
}
CutterJson CutterCore::getFileVersionInfo()
{
return cmdj("iVj");
}
QString CutterCore::getSignatureInfo()
CutterJson CutterCore::getSignatureInfo()
{
CORE_LOCK();
RzBinFile *cur = rz_bin_cur(core->bin);
@ -1462,7 +1399,17 @@ QString CutterCore::getSignatureInfo()
if (!signature) {
return {};
}
return fromOwnedCharPtr(signature);
return parseJson(signature, nullptr);
}
bool CutterCore::existsFileInfo()
{
CORE_LOCK();
const RzBinInfo *info = rz_bin_get_info(core->bin);
if (!(info && info->rclass)) {
return false;
}
return strncmp("pe", info->rclass, 2) == 0 || strncmp("elf", info->rclass, 3) == 0;
}
// Utility function to check if a telescoped item exists and add it with prefixes to the desc
@ -1846,18 +1793,7 @@ QList<VariableDescription> CutterCore::getVariables(RVA at)
}
for (auto var : CutterPVector<RzAnalysisVar>(&fcn->vars)) {
VariableDescription desc;
switch (var->kind) {
case RZ_ANALYSIS_VAR_KIND_BPV:
desc.refType = VariableDescription::RefType::BP;
break;
case RZ_ANALYSIS_VAR_KIND_SPV:
desc.refType = VariableDescription::RefType::SP;
break;
case RZ_ANALYSIS_VAR_KIND_REG:
default:
desc.refType = VariableDescription::RefType::Reg;
break;
}
desc.storageType = var->storage.type;
if (!var->name || !var->type) {
continue;
}
@ -2139,9 +2075,15 @@ void CutterCore::attachDebug(int pid)
offsetPriorDebugging = getOffset();
}
QString attach_command = currentlyOpenFile.isEmpty() ? "o" : "oodf";
// attach to process with dbg plugin
asyncCmd("e cfg.debug=true;" + attach_command + " dbg://" + QString::number(pid), debugTask);
CORE_LOCK();
setConfig("cfg.debug", true);
auto uri = rz_str_newf("dbg://%d", pid);
if (currentlyOpenFile.isEmpty()) {
rz_core_file_open_load(core, uri, 0, RZ_PERM_R, false);
} else {
rz_core_file_reopen_remote_debug(core, uri, 0);
}
free(uri);
emit debugTaskStateChanged();
@ -2195,7 +2137,8 @@ void CutterCore::stopDebug()
CORE_LOCK();
if (currentlyEmulating) {
cmdEsil("aeim- ; aei-");
rz_core_analysis_esil_init_mem_del(core, NULL, UT64_MAX, UT32_MAX);
rz_core_analysis_esil_deinit(core);
resetWriteCache();
rz_core_debug_clear_register_flags(core);
rz_core_analysis_esil_trace_stop(core);
@ -2919,11 +2862,9 @@ bool CutterCore::isGraphEmpty()
return emptyGraph;
}
void CutterCore::getOpcodes()
void CutterCore::getRegs()
{
CORE_LOCK();
this->opcodes = cmdList("?O");
this->regs = {};
const RzList *rs = rz_reg_get_list(getReg(), RZ_REG_TYPE_ANY);
if (!rs) {
@ -3099,12 +3040,8 @@ QList<FunctionDescription> CutterCore::getAllFunctions()
FunctionDescription function;
function.offset = fcn->addr;
function.linearSize = rz_analysis_function_linear_size(fcn);
function.nargs = rz_analysis_var_count(core->analysis, fcn, 'b', 1)
+ rz_analysis_var_count(core->analysis, fcn, 'r', 1)
+ rz_analysis_var_count(core->analysis, fcn, 's', 1);
function.nlocals = rz_analysis_var_count(core->analysis, fcn, 'b', 0)
+ rz_analysis_var_count(core->analysis, fcn, 'r', 0)
+ rz_analysis_var_count(core->analysis, fcn, 's', 0);
function.nargs = rz_analysis_arg_count(fcn);
function.nlocals = rz_analysis_var_local_count(fcn);
function.nbbs = rz_list_length(fcn->bbs);
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
@ -3179,21 +3116,38 @@ QList<ImportDescription> CutterCore::getAllImports()
QList<ExportDescription> CutterCore::getAllExports()
{
CORE_LOCK();
QList<ExportDescription> ret;
for (CutterJson exportObject : cmdj("iEj")) {
ExportDescription exp;
exp.vaddr = exportObject[RJsonKey::vaddr].toRVA();
exp.paddr = exportObject[RJsonKey::paddr].toRVA();
exp.size = exportObject[RJsonKey::size].toRVA();
exp.type = exportObject[RJsonKey::type].toString();
exp.name = exportObject[RJsonKey::name].toString();
exp.flag_name = exportObject[RJsonKey::flagname].toString();
ret << exp;
RzBinFile *bf = rz_bin_cur(core->bin);
if (!bf) {
return {};
}
const RzList *symbols = rz_bin_object_get_symbols(bf->o);
if (!symbols) {
return {};
}
QString lang = getConfigi("bin.demangle") ? getConfig("bin.lang") : "";
bool va = core->io->va || core->bin->is_debugger;
QList<ExportDescription> ret;
for (const auto &symbol : CutterRzList<RzBinSymbol>(symbols)) {
if (!(symbol->name && rz_core_sym_is_export(symbol))) {
continue;
}
RzBinSymNames sn = {};
rz_core_sym_name_init(core, &sn, symbol, lang.isEmpty() ? NULL : lang.toUtf8().constData());
ExportDescription exportDescription;
exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va);
exportDescription.paddr = symbol->paddr;
exportDescription.size = symbol->size;
exportDescription.type = symbol->type;
exportDescription.name = sn.symbolname;
exportDescription.flag_name = sn.nameflag;
ret << exportDescription;
rz_core_sym_name_fini(&sn);
}
return ret;
}
@ -3349,22 +3303,37 @@ QList<RelocDescription> CutterCore::getAllRelocs()
QList<StringDescription> CutterCore::getAllStrings()
{
return parseStringsJson(cmdjTask("izzj"));
}
CORE_LOCK();
RzBinFile *bf = rz_bin_cur(core->bin);
if (!bf) {
return {};
}
RzBinObject *obj = rz_bin_cur_object(core->bin);
if (!obj) {
return {};
}
RzList *l = rz_core_bin_whole_strings(core, bf);
if (!l) {
return {};
}
int va = core->io->va || core->bin->is_debugger;
RzStrEscOptions opt = {};
opt.show_asciidot = false;
opt.esc_bslash = true;
opt.esc_double_quotes = true;
QList<StringDescription> CutterCore::parseStringsJson(const CutterJson &doc)
{
QList<StringDescription> ret;
for (const auto &str : CutterRzList<RzBinString>(l)) {
auto section = obj ? rz_bin_get_section_at(obj, str->paddr, 0) : NULL;
for (CutterJson value : doc) {
StringDescription string;
string.string = value[RJsonKey::string].toString();
string.vaddr = value[RJsonKey::vaddr].toRVA();
string.type = value[RJsonKey::type].toString();
string.size = value[RJsonKey::size].toUt64();
string.length = value[RJsonKey::length].toUt64();
string.section = value[RJsonKey::section].toString();
string.string = rz_str_escape_utf8_keep_printable(str->string, &opt);
string.vaddr = obj ? rva(obj, str->paddr, str->vaddr, va) : str->paddr;
string.type = rz_str_enc_as_string(str->type);
string.size = str->size;
string.length = str->length;
string.section = section ? section->name : "";
ret << string;
}
@ -3881,7 +3850,7 @@ QList<TypeDescription> CutterCore::getBaseType(RzBaseTypeKind kind, const char *
exp.type = type->name;
exp.size = rz_type_db_base_get_bitsize(core->analysis->typedb, type);
exp.format = rz_type_format(core->analysis->typedb, type->name);
exp.format = rz_base_type_as_format(core->analysis->typedb, type);
exp.category = tr(category);
types << exp;
}
@ -3923,8 +3892,7 @@ QString CutterCore::getTypeAsC(QString name)
return output;
}
char *earg = rz_cmd_escape_arg(name.toUtf8().constData(), RZ_CMD_ESCAPE_ONE_ARG);
// TODO: use API for `tc` command once available
QString result = cmd(QString("tc %1").arg(earg));
QString result = fromOwnedCharPtr(rz_core_types_as_c(core, earg, true));
free(earg);
return result;
}
@ -3979,22 +3947,34 @@ QList<SearchDescription> CutterCore::getAllSearch(QString searchFor, QString spa
QList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites,
RVA offset)
{
CORE_LOCK();
auto fcn = functionIn(offset);
if (!fcn) {
return {};
}
const auto typ =
findWrites ? RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE : RZ_ANALYSIS_VAR_ACCESS_TYPE_READ;
QList<XrefDescription> xrefList = QList<XrefDescription>();
for (CutterJson xrefObject : cmdjAt(findWrites ? "afvWj" : "afvRj", offset)) {
QString name = xrefObject[RJsonKey::name].toString();
if (name == variableName) {
for (CutterJson address : xrefObject[RJsonKey::addrs]) {
XrefDescription xref;
RVA addr = address.toRVA();
xref.from = addr;
xref.to = addr;
if (findWrites) {
xref.from_str = RzAddressString(addr);
} else {
xref.to_str = RzAddressString(addr);
}
xrefList << xref;
for (const auto &v : CutterPVector<RzAnalysisVar>(&fcn->vars)) {
if (variableName != v->name) {
continue;
}
RzAnalysisVarAccess *acc;
CutterRzVectorForeach(&v->accesses, acc, RzAnalysisVarAccess)
{
if (!(acc->type & typ)) {
continue;
}
XrefDescription xref;
RVA addr = fcn->addr + acc->offset;
xref.from = addr;
xref.to = addr;
if (findWrites) {
xref.from_str = RzAddressString(addr);
} else {
xref.to_str = RzAddressString(addr);
}
xrefList << xref;
}
}
return xrefList;
@ -4138,18 +4118,33 @@ void CutterCore::loadPDB(const QString &file)
QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
{
CutterJson array = cmdj(QString("pdJ ") + QString::number(lines) + QString(" @ ")
+ QString::number(offset));
QList<DisassemblyLine> r;
for (CutterJson object : array) {
DisassemblyLine line;
line.offset = object[RJsonKey::offset].toRVA();
line.text = ansiEscapeToHtml(object[RJsonKey::text].toString());
line.arrow = object[RJsonKey::arrow].toRVA();
r << line;
CORE_LOCK();
auto vec = fromOwned(
rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));
if (!vec) {
return {};
}
RzCoreDisasmOptions options = {};
options.cbytes = 1;
options.vec = vec.get();
{
auto restoreSeek = seekTemp(offset);
if (rz_cons_singleton()->is_html) {
rz_cons_singleton()->is_html = false;
rz_cons_singleton()->was_html = true;
}
rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, NULL, &options);
}
QList<DisassemblyLine> r;
for (const auto &t : CutterPVector<RzAnalysisDisasmText>(vec.get())) {
DisassemblyLine line;
line.offset = t->offset;
line.text = ansiEscapeToHtml(t->text);
line.arrow = t->arrow;
r << line;
}
return r;
}
@ -4161,28 +4156,36 @@ QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
*/
QString CutterCore::hexdump(RVA address, int size, HexdumpFormats format)
{
QString command = "px";
CORE_LOCK();
char *res = nullptr;
switch (format) {
case HexdumpFormats::Normal:
res = rz_core_print_hexdump_or_hexdiff_str(core, RZ_OUTPUT_MODE_STANDARD, address, size,
false);
break;
case HexdumpFormats::Half:
command += "h";
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 2, size,
RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);
break;
case HexdumpFormats::Word:
command += "w";
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 4, size,
RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);
break;
case HexdumpFormats::Quad:
command += "q";
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 8, size,
RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);
break;
case HexdumpFormats::Signed:
command += "d";
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 1, size,
RZ_CORE_PRINT_FORMAT_TYPE_INTEGER);
break;
case HexdumpFormats::Octal:
command += "o";
res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 1, size,
RZ_CORE_PRINT_FORMAT_TYPE_OCTAL);
break;
}
return cmdRawAt(QString("%1 %2").arg(command).arg(size), address);
return fromOwnedCharPtr(res);
}
QByteArray CutterCore::hexStringToBytes(const QString &hex)
@ -4212,10 +4215,9 @@ void CutterCore::loadScript(const QString &scriptname)
triggerRefreshAll();
}
QString CutterCore::getRizinVersionReadable()
QString CutterCore::getRizinVersionReadable(const char *program)
{
return QString("%1 (%2)").arg(QString::fromUtf8(RZ_VERSION),
QString::fromUtf8(RZ_GITTIP).left(7));
return fromOwnedCharPtr(rz_version_str(program));
}
QString CutterCore::getVersionInformation()
@ -4238,7 +4240,7 @@ QString CutterCore::getVersionInformation()
{ "rz_crypto", &rz_crypto_version },
{ "rz_bp", &rz_bp_version },
{ "rz_debug", &rz_debug_version },
{ "rz_msg_digest", &rz_msg_digest_version },
{ "rz_hash", &rz_hash_version },
{ "rz_io", &rz_io_version },
#if !USE_LIB_MAGIC
{ "rz_magic", &rz_magic_version },
@ -4252,7 +4254,8 @@ QString CutterCore::getVersionInformation()
/* ... */
{ NULL, NULL }
};
versionInfo.append(QString("%1 rz\n").arg(getRizinVersionReadable()));
versionInfo.append(getRizinVersionReadable());
versionInfo.append("\n");
for (i = 0; vcs[i].name; i++) {
struct vcs_t *v = &vcs[i];
const char *name = v->callback();
@ -4316,7 +4319,7 @@ QString CutterCore::ansiEscapeToHtml(const QString &text)
int len;
char *html = rz_cons_html_filter(text.toUtf8().constData(), &len);
if (!html) {
return QString();
return {};
}
QString r = QString::fromUtf8(html, len);
rz_mem_free(html);
@ -4491,7 +4494,6 @@ QByteArray CutterCore::ioRead(RVA addr, int len)
/* Zero-copy */
array.resize(len);
if (!rz_io_read_at(core->io, addr, (uint8_t *)array.data(), len)) {
qWarning() << "Can't read data" << addr << len;
array.fill(0xff);
}
@ -4501,19 +4503,80 @@ QByteArray CutterCore::ioRead(RVA addr, int len)
QStringList CutterCore::getConfigVariableSpaces(const QString &key)
{
CORE_LOCK();
QStringList stringList;
for (const auto &node : CutterRzList<RzConfigNode>(core->config->nodes)) {
stringList.push_back(node->name);
RzList *list = rz_core_config_in_space(core, key.toUtf8().constData());
if (!list) {
return {};
}
if (!key.isEmpty()) {
stringList = stringList.filter(QRegularExpression(QString("^%0\\..*").arg(key)));
std::transform(stringList.begin(), stringList.end(), stringList.begin(),
[](const QString &x) { return x.split('.').last(); });
} else {
std::transform(stringList.begin(), stringList.end(), stringList.begin(),
[](const QString &x) { return x.split('.').first(); });
QStringList stringList;
for (const auto &x : CutterRzList<char>(list)) {
stringList << x;
}
stringList.removeDuplicates();
rz_list_free(list);
return stringList;
}
char *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat format, RVA address)
{
CORE_LOCK();
char *string = nullptr;
RzGraph *graph = rz_core_graph(core, type, address);
if (!graph) {
if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph";
} else {
qWarning() << "Cannot get graph at " << RzAddressString(address);
}
return nullptr;
}
core->graph->is_callgraph = type == RZ_CORE_GRAPH_TYPE_FUNCALL;
switch (format) {
case RZ_CORE_GRAPH_FORMAT_CMD: {
string = rz_graph_drawable_to_cmd(graph);
break;
}
case RZ_CORE_GRAPH_FORMAT_DOT: {
string = rz_core_graph_to_dot_str(core, graph);
break;
}
case RZ_CORE_GRAPH_FORMAT_JSON:
/* fall-thru */
case RZ_CORE_GRAPH_FORMAT_JSON_DISASM: {
string = rz_graph_drawable_to_json_str(graph, true);
break;
}
case RZ_CORE_GRAPH_FORMAT_GML: {
string = rz_graph_drawable_to_gml(graph);
break;
}
default:
break;
}
rz_graph_free(graph);
if (!string) {
qWarning() << "Failed to generate graph";
}
return string;
}
void CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type,
RVA address)
{
TempConfig tempConfig;
tempConfig.set("scr.color", false);
tempConfig.set("graph.gv.format", format);
CORE_LOCK();
auto filepath = path.toUtf8();
if (!rz_core_graph_write(core, address, type, filepath)) {
if (address == RVA_INVALID) {
qWarning() << "Cannot get global graph";
} else {
qWarning() << "Cannot get graph at " << RzAddressString(address);
}
}
}

View File

@ -17,6 +17,7 @@
#include <QMutex>
#include <QDir>
#include <functional>
#include <memory>
class AsyncTaskManager;
class BasicInstructionHighlighter;
@ -31,6 +32,7 @@ class RizinTaskDialog;
#include "common/Helpers.h"
#include <rz_project.h>
#include <memory>
#define Core() (CutterCore::instance())
@ -59,6 +61,8 @@ struct CUTTER_EXPORT RegisterRef
QString name;
};
using PRzAnalysisBytes = std::unique_ptr<RzAnalysisBytes, decltype(rz_analysis_bytes_free) *>;
class CUTTER_EXPORT CutterCore : public QObject
{
Q_OBJECT
@ -81,6 +85,10 @@ public:
RVA getOffset() const { return core_->offset; }
/* Core functions (commands) */
/* Almost the same as core_cmd_raw,
* only executes std::function<bool(RzCore *)> instead of char* */
QString getFunctionExecOut(const std::function<bool(RzCore *)> &fcn,
const RVA addr = RVA_INVALID);
static QString sanitizeStringForCommand(QString s);
/**
* @brief send a command to Rizin
@ -90,21 +98,6 @@ public:
*/
QString cmd(const char *str);
QString cmd(const QString &str) { return cmd(str.toUtf8().constData()); }
/**
* @brief send a command to Rizin asynchronously
* @param str the command you want to execute
* @param task a shared pointer that will be returned with the Rizin command task
* @note connect to the &RizinTask::finished signal to add your own logic once
* the command is finished. Use task->getResult()/getResultJson() for the
* return value.
* Once you have setup connections you can start the task with task->startTask()
* If you want to seek to an address, you should use CutterCore::seek.
*/
bool asyncCmd(const char *str, QSharedPointer<RizinTask> &task);
bool asyncCmd(const QString &str, QSharedPointer<RizinTask> &task)
{
return asyncCmd(str.toUtf8().constData(), task);
}
/**
* @brief send a task to Rizin
@ -112,6 +105,7 @@ public:
* @return execute successful?
*/
bool asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<RizinTask> &task);
void functionTask(std::function<void *(RzCore *)> fcn);
/**
* @brief Execute a Rizin command \a cmd. By nature, the API
@ -148,56 +142,41 @@ public:
return cmdRawAt(str.toUtf8().constData(), address);
}
void applyAtSeek(std::function<void()> fn, RVA address)
class SeekReturn
{
RVA oldOffset = getOffset();
seekSilent(address);
fn();
seekSilent(oldOffset);
}
RVA returnAddress;
bool empty = true;
void *returnAtSeek(std::function<void *()> fn, RVA address)
public:
SeekReturn(RVA returnAddress) : returnAddress(returnAddress), empty(false) {}
~SeekReturn()
{
if (!empty) {
Core()->seekSilent(returnAddress);
}
}
SeekReturn(SeekReturn &&from)
{
if (this != &from) {
returnAddress = from.returnAddress;
empty = from.empty;
from.empty = true;
}
};
};
SeekReturn seekTemp(RVA address)
{
RVA oldOffset = getOffset();
SeekReturn returner(getOffset());
seekSilent(address);
void *ret = fn();
seekSilent(oldOffset);
return ret;
return returner;
}
CutterJson cmdj(const char *str);
CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
CutterJson cmdjAt(const char *str, RVA address);
QStringList cmdList(const char *str)
{
return cmd(str).split(QLatin1Char('\n'), CUTTER_QT_SKIP_EMPTY_PARTS);
}
QStringList cmdList(const QString &str) { return cmdList(str.toUtf8().constData()); }
QString cmdTask(const QString &str);
CutterJson cmdjTask(const QString &str);
/**
* @brief send a command to Rizin and check for ESIL errors
* @param command the command you want to execute
* @note If you want to seek to an address, you should use CutterCore::seek.
*/
void cmdEsil(const char *command);
void cmdEsil(const QString &command) { cmdEsil(command.toUtf8().constData()); }
/**
* @brief send a command to Rizin and check for ESIL errors
* @param command the command you want to execute
* @param task a shared pointer that will be returned with the Rizin command task
* @note connect to the &RizinTask::finished signal to add your own logic once
* the command is finished. Use task->getResult()/getResultJson() for the
* return value.
* Once you have setup connections you can start the task with task->startTask()
* If you want to seek to an address, you should use CutterCore::seek.
*/
bool asyncCmdEsil(const char *command, QSharedPointer<RizinTask> &task);
bool asyncCmdEsil(const QString &command, QSharedPointer<RizinTask> &task)
{
return asyncCmdEsil(command.toUtf8().constData(), task);
}
QString getRizinVersionReadable();
QString getRizinVersionReadable(const char *program = nullptr);
QString getVersionInformation();
CutterJson parseJson(char *res, const char *cmd = nullptr);
@ -257,6 +236,7 @@ public:
void triggerFlagsChanged();
/* Edition functions */
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
QString getInstructionBytes(RVA addr);
QString getInstructionOpcode(RVA addr);
void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);
@ -569,14 +549,12 @@ public:
bool registerDecompiler(Decompiler *decompiler);
RVA getOffsetJump(RVA addr);
CutterJson getFileInfo();
QString getSignatureInfo();
CutterJson getFileVersionInfo();
CutterJson getSignatureInfo();
bool existsFileInfo();
void setGraphEmpty(bool empty);
bool isGraphEmpty();
void getOpcodes();
QList<QString> opcodes;
void getRegs();
QList<QString> regs;
void setSettings();
@ -688,8 +666,6 @@ public:
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
const QString &filterType = QString());
QList<StringDescription> parseStringsJson(const CutterJson &doc);
void handleREvent(int type, void *data);
/* Signals related */
@ -744,6 +720,25 @@ public:
*/
bool isWriteModeEnabled();
/**
* @brief Returns the textual version of global or specific graph.
* @param type Graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
* @param format Graph format, example RZ_CORE_GRAPH_FORMAT_DOT or RZ_CORE_GRAPH_FORMAT_GML
* @param address The object address (if global set it to RVA_INVALID)
* @return The textual graph string.
*/
char *getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat format, RVA address);
/**
* @brief Writes a graphviz graph to a file.
* @param path The file output path
* @param format The output format (see graph.gv.format)
* @param type The graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or
* RZ_CORE_GRAPH_TYPE_IMPORT
* @param address The object address (if global set it to RVA_INVALID)
*/
void writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type, RVA address);
signals:
void refreshAll();

View File

@ -7,6 +7,7 @@
#include "rz_core.h"
#include <QString>
#include "RizinCpp.h"
// Workaround for compile errors on Windows
#ifdef Q_OS_WIN
@ -14,103 +15,6 @@
# undef max
#endif // Q_OS_WIN
// Rizin list iteration macros
#define CutterRzListForeach(list, it, type, x) \
if (list) \
for (it = list->head; it && ((x = static_cast<type *>(it->data))); it = it->n)
#define CutterRzVectorForeach(vec, it, type) \
if ((vec) && (vec)->a) \
for (it = (type *)(vec)->a; \
(char *)it != (char *)(vec)->a + ((vec)->len * (vec)->elem_size); \
it = (type *)((char *)it + (vec)->elem_size))
template<typename T>
class CutterPVector
{
private:
const RzPVector *const vec;
public:
class iterator : public std::iterator<std::input_iterator_tag, T *>
{
private:
T **p;
public:
iterator(T **p) : p(p) {}
iterator(const iterator &o) : p(o.p) {}
iterator &operator++()
{
p++;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const iterator &rhs) const { return p == rhs.p; }
bool operator!=(const iterator &rhs) const { return p != rhs.p; }
T *operator*() { return *p; }
};
CutterPVector(const RzPVector *vec) : vec(vec) {}
iterator begin() const { return iterator(reinterpret_cast<T **>(vec->v.a)); }
iterator end() const { return iterator(reinterpret_cast<T **>(vec->v.a) + vec->v.len); }
};
template<typename T>
class CutterRzList
{
private:
const RzList *const list;
public:
class iterator : public std::iterator<std::input_iterator_tag, T *>
{
private:
RzListIter *iter;
public:
explicit iterator(RzListIter *iter) : iter(iter) {}
iterator(const iterator &o) : iter(o.iter) {}
iterator &operator++()
{
if (!iter) {
return *this;
}
iter = iter->n;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const iterator &rhs) const { return iter == rhs.iter; }
bool operator!=(const iterator &rhs) const { return iter != rhs.iter; }
T *operator*()
{
if (!iter) {
return nullptr;
}
return reinterpret_cast<T *>(iter->data);
}
};
explicit CutterRzList(const RzList *l) : list(l) {}
iterator begin() const
{
if (!list) {
return iterator(nullptr);
}
return iterator(list->head);
}
iterator end() const { return iterator(nullptr); }
};
// Global information for Cutter
#define APPNAME "Cutter"

View File

@ -355,8 +355,7 @@ struct RefDescription
struct VariableDescription
{
enum class RefType { SP, BP, Reg };
RefType refType;
RzAnalysisVarStorageType storageType;
QString name;
QString type;
};

View File

@ -70,7 +70,6 @@ public:
iterator end() const { return iterator(nullptr, nullptr); }
bool toBool() const { return value && value->type == RZ_JSON_BOOLEAN && value->num.u_value; }
QString toJson() const { return rz_json_as_string(value); }
st64 toSt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.s_value : 0; }
ut64 toUt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.u_value : 0; }

View File

@ -149,7 +149,7 @@ void MainWindow::initUI()
&MainWindow::addExtraDisassembly);
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
connect(ui->actionCommitChanges, &QAction::triggered, this,
[this]() { Core()->commitWriteCache(); });
[]() { Core()->commitWriteCache(); });
ui->actionCommitChanges->setEnabled(false);
connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled);
@ -637,7 +637,7 @@ bool MainWindow::openProject(const QString &file)
void MainWindow::finalizeOpen()
{
core->getOpcodes();
core->getRegs();
core->updateSeek();
refreshAll();
// Add fortune message
@ -1086,6 +1086,12 @@ MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA addr
case MemoryWidgetType::Decompiler:
memoryWidget = new DecompilerWidget(this);
break;
case MemoryWidgetType::CallGraph:
memoryWidget = new CallGraphWidget(this, false);
break;
case MemoryWidgetType::GlobalCallGraph:
memoryWidget = new CallGraphWidget(this, true);
break;
}
auto seekable = memoryWidget->getSeekable();
seekable->setSynchronization(synchronized);
@ -1108,7 +1114,7 @@ void MainWindow::initBackForwardMenu()
connect(button, &QWidget::customContextMenuRequested, button,
[menu, button](const QPoint &pos) { menu->exec(button->mapToGlobal(pos)); });
QFontMetrics metrics(fontMetrics());
QFontMetrics metrics(font());
// Roughly 10-16 lines depending on padding size, no need to calculate more precisely
menu->setMaximumHeight(metrics.lineSpacing() * 20);
@ -1692,35 +1698,55 @@ void MainWindow::on_actionImportPDB_triggered()
}
}
#define TYPE_BIG_ENDIAN(type, big_endian) big_endian ? type##_BE : type##_LE
void MainWindow::on_actionExport_as_code_triggered()
{
QStringList filters;
QMap<QString, QString> cmdMap;
QMap<QString, RzLangByteArrayType> typMap;
const bool big_endian = Core()->getConfigb("big_endian");
filters << tr("C uin8_t array (*.c)");
cmdMap[filters.last()] = "pc";
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_C_CPP_BYTES;
filters << tr("C uin16_t array (*.c)");
cmdMap[filters.last()] = "pch";
typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_HALFWORDS, big_endian);
filters << tr("C uin32_t array (*.c)");
cmdMap[filters.last()] = "pcw";
typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_WORDS, big_endian);
filters << tr("C uin64_t array (*.c)");
cmdMap[filters.last()] = "pcd";
filters << tr("C string (*.c)");
cmdMap[filters.last()] = "pcs";
filters << tr("Shell-script that reconstructs the bin (*.sh)");
cmdMap[filters.last()] = "pcS";
typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_DOUBLEWORDS, big_endian);
filters << tr("Go array (*.go)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_GOLANG;
filters << tr("Java array (*.java)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_JAVA;
filters << tr("JSON array (*.json)");
cmdMap[filters.last()] = "pcj";
filters << tr("JavaScript array (*.js)");
cmdMap[filters.last()] = "pcJ";
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_JSON;
filters << tr("Kotlin array (*.kt)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_KOTLIN;
filters << tr("Javascript array (*.js)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_NODEJS;
filters << tr("ObjectiveC array (*.m)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_OBJECTIVE_C;
filters << tr("Python array (*.py)");
cmdMap[filters.last()] = "pcp";
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_PYTHON;
filters << tr("Rust array (*.rs)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_RUST;
filters << tr("Swift array (*.swift)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_SWIFT;
filters << tr("Print 'wx' Rizin commands (*.rz)");
cmdMap[filters.last()] = "pc*";
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_RIZIN;
filters << tr("Shell-script that reconstructs the bin (*.sh)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_BASH;
filters << tr("GAS .byte blob (*.asm, *.s)");
cmdMap[filters.last()] = "pca";
filters << tr(".bytes with instructions in comments (*.txt)");
cmdMap[filters.last()] = "pcA";
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_ASM;
filters << tr("Yara (*.yar)");
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_YARA;
/* special case */
QString instructionsInComments = tr(".bytes with instructions in comments (*.txt)");
filters << instructionsInComments;
QFileDialog dialog(this, tr("Export as code"));
dialog.setAcceptMode(QFileDialog::AcceptSave);
@ -1736,13 +1762,25 @@ void MainWindow::on_actionExport_as_code_triggered()
qWarning() << "Can't open file";
return;
}
TempConfig tempConfig;
tempConfig.set("io.va", false);
QTextStream fileOut(&file);
QString &cmd = cmdMap[dialog.selectedNameFilter()];
auto ps = core->seekTemp(0);
auto rc = core->core();
const auto size = static_cast<int>(rz_io_fd_size(rc->io, rc->file->fd));
auto buffer = std::vector<ut8>(size);
if (!rz_io_read_at(Core()->core()->io, 0, buffer.data(), size)) {
return;
}
// Use cmd because cmdRaw would not handle such input
fileOut << Core()->cmd(cmd + " $s @ 0");
auto string = fromOwned(
dialog.selectedNameFilter() != instructionsInComments
? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()])
: rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size));
fileOut << string.get();
}
void MainWindow::on_actionApplySigFromFile_triggered()

2
src/core/RizinCpp.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "RizinCpp.h"

168
src/core/RizinCpp.h Normal file
View File

@ -0,0 +1,168 @@
/** \file RizinCpp.h
* Various utilities for easier and safer interactions with Rizin
* from C++ code.
*/
#ifndef RIZINCPP_H
#define RIZINCPP_H
#include "rz_core.h"
#include <QString>
#include <memory>
static inline QString fromOwnedCharPtr(char *str)
{
QString result(str ? str : "");
rz_mem_free(str);
return result;
}
template<class T, class F>
std::unique_ptr<T, F *> fromOwned(T *data, F *freeFunction)
{
return std::unique_ptr<T, F *> { data, freeFunction };
}
static inline std::unique_ptr<char, decltype(&rz_mem_free)> fromOwned(char *text)
{
return { text, rz_mem_free };
}
template<class T, void (*func)(T *)>
class FreeBinder
{
public:
void operator()(T *data) { func(data); }
};
template<class T, void (*func)(T *)>
using UniquePtrC = std::unique_ptr<T, FreeBinder<T, func>>;
template<typename T, void (*func)(T)>
using UniquePtrCP = UniquePtrC<typename std::remove_pointer<T>::type, func>;
static inline auto fromOwned(RZ_OWN RzPVector *data)
-> UniquePtrCP<decltype(data), &rz_pvector_free>
{
return { data, {} };
}
static inline auto fromOwned(RZ_OWN RzList *data)
-> UniquePtrCP<decltype(data), &rz_list_free>
{
return { data, {} };
}
// Rizin list iteration macros
// deprecated, prefer using CutterPVector and CutterRzList instead
#define CutterRzListForeach(list, it, type, x) \
if (list) \
for (it = list->head; it && ((x = static_cast<type *>(it->data))); it = it->n)
#define CutterRzVectorForeach(vec, it, type) \
if ((vec) && (vec)->a) \
for (it = (type *)(vec)->a; \
(char *)it != (char *)(vec)->a + ((vec)->len * (vec)->elem_size); \
it = (type *)((char *)it + (vec)->elem_size))
template<typename T>
class CutterPVector
{
private:
const RzPVector *const vec;
public:
class iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = T *;
using difference_type = ptrdiff_t;
using pointer = T **;
using reference = T *&;
private:
T **p;
public:
iterator(T **p) : p(p) {}
iterator(const iterator &o) : p(o.p) {}
iterator &operator++()
{
p++;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const iterator &rhs) const { return p == rhs.p; }
bool operator!=(const iterator &rhs) const { return p != rhs.p; }
T *operator*() { return *p; }
};
CutterPVector(const RzPVector *vec) : vec(vec) {}
iterator begin() const { return iterator(reinterpret_cast<T **>(vec->v.a)); }
iterator end() const { return iterator(reinterpret_cast<T **>(vec->v.a) + vec->v.len); }
};
template<typename T>
class CutterRzList
{
private:
const RzList *const list;
public:
class iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = T *;
using difference_type = ptrdiff_t;
using pointer = T **;
using reference = T *&;
private:
RzListIter *iter;
public:
explicit iterator(RzListIter *iter) : iter(iter) {}
iterator(const iterator &o) : iter(o.iter) {}
iterator &operator++()
{
if (!iter) {
return *this;
}
iter = iter->n;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const iterator &rhs) const { return iter == rhs.iter; }
bool operator!=(const iterator &rhs) const { return iter != rhs.iter; }
T *operator*()
{
if (!iter) {
return nullptr;
}
return reinterpret_cast<T *>(iter->data);
}
};
explicit CutterRzList(const RzList *l) : list(l) {}
iterator begin() const
{
if (!list) {
return iterator(nullptr);
}
return iterator(list->head);
}
iterator end() const { return iterator(nullptr); }
};
#endif // RIZINCPP_H

View File

@ -1,4 +1,3 @@
#include "rz_version.h"
#include "core/Cutter.h"
#include "AboutDialog.h"
@ -7,7 +6,6 @@
#include "common/Configuration.h"
#include "common/BugReporting.h"
#include <QUrl>
#include <QTimer>
#include <QEventLoop>

View File

@ -86,11 +86,7 @@ bool EditMethodDialog::inputValid()
QString EditMethodDialog::convertRealNameToName(const QString &realName)
{
std::unique_ptr<const char, void (*)(const char *)> sanitizedCString(
rz_str_sanitize_sdb_key(realName.toUtf8().constData()),
[](const char *s) { rz_mem_free((void*)s); });
return QString(sanitizedCString.get());
return fromOwnedCharPtr(rz_str_sanitize_sdb_key(realName.toUtf8().constData()));
}
void EditMethodDialog::setClass(const QString &className)

View File

@ -22,7 +22,6 @@ TypesInteractionDialog::TypesInteractionDialog(QWidget *parent, bool readOnly)
syntaxHighLighter = Config()->createSyntaxHighlighter(ui->plainTextEdit->document());
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
ui->plainTextEdit->setReadOnly(readOnly);
this->typeName = "";
}
TypesInteractionDialog::~TypesInteractionDialog() {}
@ -64,10 +63,21 @@ void TypesInteractionDialog::done(int r)
{
if (r == QDialog::Accepted) {
RzCoreLocked core(Core());
bool edited = rz_type_db_edit_base_type(
bool success;
if (!typeName.isEmpty()) {
success = rz_type_db_edit_base_type(
core->analysis->typedb, this->typeName.toUtf8().constData(),
ui->plainTextEdit->toPlainText().toUtf8().constData());
if (edited) {
} else {
char *error_msg = NULL;
success = rz_type_parse_string_stateless(core->analysis->typedb->parser,
ui->plainTextEdit->toPlainText().toUtf8().constData(), &error_msg) == 0;
if (error_msg) {
RZ_LOG_ERROR("%s\n", error_msg);
rz_mem_free(error_msg);
}
}
if (success) {
emit newTypesLoaded();
QDialog::done(r);
return;

View File

@ -23,145 +23,213 @@ VersionInfoDialog::~VersionInfoDialog() {}
void VersionInfoDialog::fillVersionInfo()
{
CutterJson doc = Core()->getFileVersionInfo();
RzCoreLocked core(Core());
const RzBinInfo *info = rz_bin_get_info(core->bin);
if (!info || !info->rclass) {
return;
}
// Case ELF
if (doc["verneed"].valid()) {
CutterJson verneed = doc["verneed"].first();
CutterJson versym = doc["versym"].first();
if (strncmp("elf", info->rclass, 3) == 0) {
// Set labels
ui->leftLabel->setText("Version symbols");
ui->rightLabel->setText("Version need");
// Left tree
QTreeWidgetItem *secNameItemL = new QTreeWidgetItem();
secNameItemL->setText(0, "Section name:");
secNameItemL->setText(1, versym["section_name"].toString());
ui->leftTreeWidget->addTopLevelItem(secNameItemL);
Sdb *sdb = sdb_ns_path(core->sdb, "bin/cur/info/versioninfo/versym", 0);
if (!sdb) {
return;
}
// Left tree
QTreeWidgetItem *addrItemL = new QTreeWidgetItem();
addrItemL->setText(0, "Address:");
addrItemL->setText(1, RzAddressString(versym["address"].toRVA()));
addrItemL->setText(1, RzAddressString(sdb_num_get(sdb, "addr", 0)));
ui->leftTreeWidget->addTopLevelItem(addrItemL);
QTreeWidgetItem *offItemL = new QTreeWidgetItem();
offItemL->setText(0, "Offset:");
offItemL->setText(1, RzAddressString(versym["offset"].toRVA()));
offItemL->setText(1, RzAddressString(sdb_num_get(sdb, "offset", 0)));
ui->leftTreeWidget->addTopLevelItem(offItemL);
QTreeWidgetItem *linkItemL = new QTreeWidgetItem();
linkItemL->setText(0, "Link:");
linkItemL->setText(1, QString::number(versym["link"].toRVA()));
ui->leftTreeWidget->addTopLevelItem(linkItemL);
QTreeWidgetItem *linkNameItemL = new QTreeWidgetItem();
linkNameItemL->setText(0, "Link section name:");
linkNameItemL->setText(1, versym["link_section_name"].toString());
ui->leftTreeWidget->addTopLevelItem(linkNameItemL);
QTreeWidgetItem *entriesItemL = new QTreeWidgetItem();
entriesItemL->setText(0, "Entries:");
for (CutterJson obj : versym["entries"]) {
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
tempItem->setText(0, RzAddressString(obj["idx"].toRVA()));
tempItem->setText(1, obj["value"].toString());
entriesItemL->addChild(tempItem);
const ut64 num_entries = sdb_num_get(sdb, "num_entries", 0);
for (size_t i = 0; i < num_entries; ++i) {
auto key = QString("entry%0").arg(i);
const char *const value = sdb_const_get(sdb, key.toStdString().c_str(), 0);
if (!value) {
continue;
}
auto item = new QTreeWidgetItem();
item->setText(0, RzAddressString(i));
item->setText(1, value);
entriesItemL->addChild(item);
}
ui->leftTreeWidget->addTopLevelItem(entriesItemL);
// Adjust columns to content
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
sdb = sdb_ns_path(core->sdb, "bin/cur/info/versioninfo/verneed", 0);
// Right tree
QTreeWidgetItem *secNameItemR = new QTreeWidgetItem();
secNameItemR->setText(0, "Section name:");
secNameItemR->setText(1, verneed["section_name"].toString());
ui->rightTreeWidget->addTopLevelItem(secNameItemR);
QTreeWidgetItem *addrItemR = new QTreeWidgetItem();
addrItemR->setText(0, "Address:");
addrItemR->setText(1, RzAddressString(verneed["address"].toRVA()));
addrItemR->setText(1, RzAddressString(sdb_num_get(sdb, "addr", 0)));
ui->rightTreeWidget->addTopLevelItem(addrItemR);
QTreeWidgetItem *offItemR = new QTreeWidgetItem();
offItemR->setText(0, "Offset:");
offItemR->setText(1, RzAddressString(verneed["offset"].toRVA()));
offItemR->setText(1, RzAddressString(sdb_num_get(sdb, "offset", 0)));
ui->rightTreeWidget->addTopLevelItem(offItemR);
QTreeWidgetItem *linkItemR = new QTreeWidgetItem();
linkItemR->setText(0, "Link:");
linkItemR->setText(1, QString::number(verneed["link"].toSt64()));
ui->rightTreeWidget->addTopLevelItem(linkItemR);
QTreeWidgetItem *linkNameItemR = new QTreeWidgetItem();
linkNameItemR->setText(0, "Link section name:");
linkNameItemR->setText(1, verneed["link_section_name"].toString());
ui->rightTreeWidget->addTopLevelItem(linkNameItemR);
QTreeWidgetItem *entriesItemR = new QTreeWidgetItem();
entriesItemR->setText(0, "Entries:");
for (CutterJson parentObj : verneed["entries"]) {
QTreeWidgetItem *parentItem = new QTreeWidgetItem();
QString parentString;
parentItem->setText(0, RzAddressString(parentObj["idx"].toRVA()));
parentString.append("Version: " + QString::number(parentObj["vn_version"].toSt64())
+ "\t");
parentString.append("File: " + parentObj["file_name"].toString());
parentItem->setText(1, parentString);
for (size_t num_version = 0;; num_version++) {
auto path_version =
QString("bin/cur/info/versioninfo/verneed/version%0").arg(num_version);
sdb = sdb_ns_path(core->sdb, path_version.toStdString().c_str(), 0);
if (!sdb) {
break;
}
const char *filename = sdb_const_get(sdb, "file_name", 0);
auto *parentItem = new QTreeWidgetItem();
parentItem->setText(0, RzAddressString(sdb_num_get(sdb, "idx", 0)));
parentItem->setText(1,
QString("Version: %0\t"
"File: %1")
.arg(QString::number(sdb_num_get(sdb, "vn_version", 0)),
QString(filename)));
for (CutterJson childObj : parentObj["vernaux"]) {
QTreeWidgetItem *childItem = new QTreeWidgetItem();
QString childString;
childItem->setText(0, RzAddressString(childObj["idx"].toRVA()));
childString.append("Name: " + childObj["name"].toString() + "\t");
childString.append("Flags: " + childObj["flags"].toString() + "\t");
childString.append("Version: " + QString::number(childObj["version"].toSt64()));
int num_vernaux = 0;
while (true) {
auto path_vernaux =
QString("%0/vernaux%1").arg(path_version, QString::number(num_vernaux++));
sdb = sdb_ns_path(core->sdb, path_vernaux.toStdString().c_str(), 0);
if (!sdb) {
break;
}
auto *childItem = new QTreeWidgetItem();
childItem->setText(0, RzAddressString(sdb_num_get(sdb, "idx", 0)));
QString childString =
QString("Name: %0\t"
"Flags: %1\t"
"Version: %2\t")
.arg(sdb_const_get(sdb, "name", 0), sdb_const_get(sdb, "flags", 0),
QString::number(sdb_num_get(sdb, "version", 0)));
childItem->setText(1, childString);
parentItem->addChild(childItem);
}
entriesItemR->addChild(parentItem);
}
ui->rightTreeWidget->addTopLevelItem(entriesItemR);
ui->rightTreeWidget->addTopLevelItem(entriesItemR);
// Adjust columns to content
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
}
// Case PE
else if (doc["VS_FIXEDFILEINFO"].valid()) {
CutterJson vs = doc["VS_FIXEDFILEINFO"];
CutterJson strings = doc["StringTable"];
else if (strncmp("pe", info->rclass, 2) == 0) {
// Set labels
ui->leftLabel->setText("VS Fixed file info");
ui->rightLabel->setText("String table");
Sdb *sdb = NULL;
// Left tree
for (CutterJson property : vs) {
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
tempItem->setText(0, property.key());
if (property.type() == RZ_JSON_INTEGER)
tempItem->setText(1, RzHexString(property.toRVA()));
else
tempItem->setText(1, property.toString());
ui->leftTreeWidget->addTopLevelItem(tempItem);
// Adjust columns to content
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
auto path_version = QString("bin/cur/info/vs_version_info/VS_VERSIONINFO%0").arg(0);
auto path_fixedfileinfo = QString("%0/fixed_file_info").arg(path_version);
sdb = sdb_ns_path(core->sdb, path_fixedfileinfo.toStdString().c_str(), 0);
if (!sdb) {
return;
}
ut32 file_version_ms = sdb_num_get(sdb, "FileVersionMS", 0);
ut32 file_version_ls = sdb_num_get(sdb, "FileVersionLS", 0);
auto file_version = QString("%0.%1.%2.%3")
.arg(file_version_ms >> 16)
.arg(file_version_ms & 0xFFFF)
.arg(file_version_ls >> 16)
.arg(file_version_ls & 0xFFFF);
ut32 product_version_ms = sdb_num_get(sdb, "ProductVersionMS", 0);
ut32 product_version_ls = sdb_num_get(sdb, "ProductVersionLS", 0);
auto product_version = QString("%0.%1.%2.%3")
.arg(product_version_ms >> 16)
.arg(product_version_ms & 0xFFFF)
.arg(product_version_ls >> 16)
.arg(product_version_ls & 0xFFFF);
auto item = new QTreeWidgetItem();
item->setText(0, "Signature");
item->setText(1, RzHexString(sdb_num_get(sdb, "Signature", 0)));
ui->leftTreeWidget->addTopLevelItem(item);
item = new QTreeWidgetItem();
item->setText(0, "StrucVersion");
item->setText(1, RzHexString(sdb_num_get(sdb, "StrucVersion", 0)));
ui->leftTreeWidget->addTopLevelItem(item);
item = new QTreeWidgetItem();
item->setText(0, "FileVersion");
item->setText(1, file_version);
ui->leftTreeWidget->addTopLevelItem(item);
item = new QTreeWidgetItem();
item->setText(0, "ProductVersion");
item->setText(1, product_version);
ui->leftTreeWidget->addTopLevelItem(item);
item = new QTreeWidgetItem();
item->setText(0, "FileFlagsMask");
item->setText(1, RzHexString(sdb_num_get(sdb, "FileFlagsMask", 0)));
ui->leftTreeWidget->addTopLevelItem(item);
item = new QTreeWidgetItem();
item->setText(0, "FileFlags");
item->setText(1, RzHexString(sdb_num_get(sdb, "FileFlags", 0)));
ui->leftTreeWidget->addTopLevelItem(item);
item = new QTreeWidgetItem();
item->setText(0, "FileOS");
item->setText(1, RzHexString(sdb_num_get(sdb, "FileOS", 0)));
ui->leftTreeWidget->addTopLevelItem(item);
item = new QTreeWidgetItem();
item->setText(0, "FileType");
item->setText(1, RzHexString(sdb_num_get(sdb, "FileType", 0)));
ui->leftTreeWidget->addTopLevelItem(item);
item = new QTreeWidgetItem();
item->setText(0, "FileSubType");
item->setText(1, RzHexString(sdb_num_get(sdb, "FileSubType", 0)));
ui->leftTreeWidget->addTopLevelItem(item);
// Adjust columns to content
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
// Right tree
for (CutterJson property : strings) {
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
tempItem->setText(0, property.key());
tempItem->setText(1, property.toString());
ui->rightTreeWidget->addTopLevelItem(tempItem);
// Adjust columns to content
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
for (int num_stringtable = 0;; num_stringtable++) {
auto path_stringtable = QString("%0/string_file_info/stringtable%1")
.arg(path_version)
.arg(num_stringtable);
sdb = sdb_ns_path(core->sdb, path_stringtable.toStdString().c_str(), 0);
if (!sdb) {
break;
}
for (int num_string = 0; sdb; num_string++) {
auto path_string = QString("%0/string%1").arg(path_stringtable).arg(num_string);
sdb = sdb_ns_path(core->sdb, path_string.toStdString().c_str(), 0);
if (!sdb) {
continue;
}
int lenkey = 0;
int lenval = 0;
ut8 *key_utf16 = sdb_decode(sdb_const_get(sdb, "key", 0), &lenkey);
ut8 *val_utf16 = sdb_decode(sdb_const_get(sdb, "value", 0), &lenval);
item = new QTreeWidgetItem();
item->setText(0, QString::fromUtf16(reinterpret_cast<const ushort *>(key_utf16)));
item->setText(1, QString::fromUtf16(reinterpret_cast<const ushort *>(val_utf16)));
ui->rightTreeWidget->addTopLevelItem(item);
free(key_utf16);
free(val_utf16);
}
}
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
}
}

View File

@ -131,8 +131,18 @@ void XrefsDialog::updatePreview(RVA addr)
tempConfig.set("asm.lines", false);
tempConfig.set("asm.bytes", false);
// Use cmd because cmRaw cannot handle the output properly. Why?
QString disas = Core()->cmd("pd--20 @ " + QString::number(addr));
QString disas = Core()->getFunctionExecOut(
[](RzCore *core) {
ut64 offset = core->offset;
if (!rz_core_prevop_addr(core, core->offset, 20, &offset)) {
offset = rz_core_prevop_addr_force(core, core->offset, 20);
}
rz_core_seek(core, offset, true);
rz_core_print_disasm(core, core->offset, core->block, (int)core->blocksize, 40,
NULL, NULL);
return true;
},
addr);
ui->previewTextEdit->document()->setHtml(disas);
// Does it make any sense?

View File

@ -69,6 +69,11 @@ DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWi
DecompilerContextMenu::~DecompilerContextMenu() {}
QWidget *DecompilerContextMenu::parentForDialog()
{
return parentWidget();
}
void DecompilerContextMenu::setAnnotationHere(RzCodeAnnotation *annotation)
{
annotationHere = annotation;
@ -404,14 +409,15 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
RzAnalysisFunction *func = Core()->functionAt(func_addr);
if (func == NULL) {
QString function_name = QInputDialog::getText(
this, tr("Define this function at %2").arg(RzAddressString(func_addr)),
parentForDialog(),
tr("Define this function at %2").arg(RzAddressString(func_addr)),
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
if (ok && !function_name.isEmpty()) {
Core()->createFunctionAt(func_addr, function_name);
}
} else {
QString newName = QInputDialog::getText(
this->mainWindow, tr("Rename function %2").arg(currentName),
parentForDialog(), tr("Rename function %2").arg(currentName),
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
if (ok && !newName.isEmpty()) {
Core()->renameFunction(func_addr, newName);
@ -421,16 +427,16 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
RVA var_addr = annotationHere->reference.offset;
RzFlagItem *flagDetails = rz_flag_get_i(core->flags, var_addr);
if (flagDetails) {
QString newName = QInputDialog::getText(this, tr("Rename %2").arg(flagDetails->name),
tr("Enter name"), QLineEdit::Normal,
flagDetails->name, &ok);
QString newName = QInputDialog::getText(
parentForDialog(), tr("Rename %2").arg(flagDetails->name), tr("Enter name"),
QLineEdit::Normal, flagDetails->name, &ok);
if (ok && !newName.isEmpty()) {
Core()->renameFlag(flagDetails->name, newName);
}
} else {
QString newName = QInputDialog::getText(
this, tr("Add name to %2").arg(curHighlightedWord), tr("Enter name"),
QLineEdit::Normal, curHighlightedWord, &ok);
parentForDialog(), tr("Add name to %2").arg(curHighlightedWord),
tr("Enter name"), QLineEdit::Normal, curHighlightedWord, &ok);
if (ok && !newName.isEmpty()) {
Core()->addFlag(var_addr, newName, 1);
}
@ -439,14 +445,14 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
if (!variablePresentInRizin()) {
// Show can't rename this variable dialog
QMessageBox::critical(
this,
parentForDialog(),
tr("Rename local variable %1").arg(QString(annotationHere->variable.name)),
tr("Can't rename this variable. "
"Only local variables defined in disassembly can be renamed."));
return;
}
QString oldName(annotationHere->variable.name);
QString newName = QInputDialog::getText(this, tr("Rename %2").arg(oldName),
QString newName = QInputDialog::getText(parentForDialog(), tr("Rename %2").arg(oldName),
tr("Enter name"), QLineEdit::Normal, oldName, &ok);
if (ok && !newName.isEmpty()) {
Core()->renameFunctionVariable(newName, oldName, decompiledFunctionAddress);
@ -465,13 +471,14 @@ void DecompilerContextMenu::actionEditFunctionVariablesTriggered()
return;
} else if (!variablePresentInRizin()) {
QMessageBox::critical(
this, tr("Edit local variable %1").arg(QString(annotationHere->variable.name)),
parentForDialog(),
tr("Edit local variable %1").arg(QString(annotationHere->variable.name)),
tr("Can't edit this variable. "
"Only local variables defined in disassembly can be edited."));
return;
}
EditVariablesDialog dialog(decompiledFunctionAddress, QString(annotationHere->variable.name),
this);
parentForDialog());
dialog.exec();
}

View File

@ -111,6 +111,12 @@ private:
QAction actionSetPC;
// Private Functions
/**
* \return widget that should be used as parent for presenting dialogs
*/
QWidget *parentForDialog();
/**
* @brief Sets the shortcut context in all the actions contained
* in the specified QMenu to Qt::WidgetWithChildrenShortcut.

View File

@ -313,34 +313,33 @@ void DisassemblyContextMenu::addDebugMenu()
QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
{
QVector<ThingUsedHere> result;
const CutterJson array = Core()->cmdj("anj @ " + QString::number(offset));
result.reserve(array.size());
for (const auto &thing : array) {
auto obj = thing;
RVA offset = obj["offset"].toRVA();
QString name;
// If real names display is enabled, show flag's real name instead of full flag name
if (Config()->getConfigBool("asm.flags.real") && obj["realname"].valid()) {
name = obj["realname"].toString();
} else {
name = obj["name"].toString();
}
QString typeString = obj["type"].toString();
ThingUsedHere::Type type = ThingUsedHere::Type::Address;
if (typeString == "var") {
type = ThingUsedHere::Type::Var;
} else if (typeString == "flag") {
type = ThingUsedHere::Type::Flag;
} else if (typeString == "function") {
type = ThingUsedHere::Type::Function;
} else if (typeString == "address") {
type = ThingUsedHere::Type::Address;
}
result.push_back(ThingUsedHere { name, offset, type });
RzCoreLocked core(Core());
auto p = fromOwned(
rz_core_analysis_name(core, offset), rz_core_analysis_name_free);
if (!p) {
return {};
}
QVector<ThingUsedHere> result;
ThingUsedHere th;
th.offset = p->offset;
th.name = Config()->getConfigBool("asm.flags.real") && p->realname ? p->realname : p->name;
switch (p->type) {
case RZ_CORE_ANALYSIS_NAME_TYPE_FLAG:
th.type = ThingUsedHere::Type::Flag;
break;
case RZ_CORE_ANALYSIS_NAME_TYPE_FUNCTION:
th.type = ThingUsedHere::Type::Function;
break;
case RZ_CORE_ANALYSIS_NAME_TYPE_VAR:
th.type = ThingUsedHere::Type::Var;
break;
case RZ_CORE_ANALYSIS_NAME_TYPE_ADDRESS:
default:
th.type = ThingUsedHere::Type::Address;
break;
}
result.push_back(th);
return result;
}
@ -482,13 +481,7 @@ void DisassemblyContextMenu::setupRenaming()
void DisassemblyContextMenu::aboutToShowSlot()
{
// check if set immediate base menu makes sense
RzPVector *vec = (RzPVector *)Core()->returnAtSeek(
[&]() {
RzCoreLocked core(Core());
return rz_core_analysis_bytes(core, core->block, (int)core->blocksize, 1);
},
offset);
auto *ab = static_cast<RzAnalysisBytes *>(rz_pvector_head(vec));
auto ab = Core()->getRzAnalysisBytesSingle(offset);
bool immBase = ab && ab->op && (ab->op->val || ab->op->ptr);
setBaseMenu->menuAction()->setVisible(immBase);
@ -514,7 +507,6 @@ void DisassemblyContextMenu::aboutToShowSlot()
}
}
}
rz_pvector_free(vec);
if (memBaseReg.isEmpty()) {
// hide structure offset menu
@ -532,7 +524,7 @@ void DisassemblyContextMenu::aboutToShowSlot()
continue;
}
structureOffsetMenu->addAction("[" + memBaseReg + " + " + ty->path + "]")
->setData(ty->path);
->setData(QString(ty->path));
}
rz_list_free(typeoffs);
}
@ -726,18 +718,13 @@ void DisassemblyContextMenu::on_actionNopInstruction_triggered()
void DisassemblyContextMenu::showReverseJmpQuery()
{
QString type;
CutterJson array = Core()->cmdj("pdj 1 @ " + RzAddressString(offset));
if (!array.size()) {
actionJmpReverse.setVisible(false);
auto ab = Core()->getRzAnalysisBytesSingle(offset);
if (!(ab && ab->op)) {
return;
}
type = array.first()["type"].toString();
if (type == "cjmp") {
if (ab->op->type == RZ_ANALYSIS_OP_TYPE_CJMP) {
actionJmpReverse.setVisible(true);
} else {
actionJmpReverse.setVisible(false);
}
}
@ -1036,18 +1023,15 @@ void DisassemblyContextMenu::on_actionEditFunction_triggered()
if (dialog.exec()) {
QString new_name = dialog.getNameText();
Core()->renameFunction(fcn->addr, new_name);
rz_core_analysis_function_rename(core, fcn->addr, new_name.toStdString().c_str());
QString new_start_addr = dialog.getStartAddrText();
fcn->addr = Core()->math(new_start_addr);
QString new_stack_size = dialog.getStackSizeText();
fcn->stack = int(Core()->math(new_stack_size));
const char *ccSelected = dialog.getCallConSelected().toUtf8().constData();
if (RZ_STR_ISEMPTY(ccSelected)) {
return;
}
if (rz_analysis_cc_exist(core->analysis, ccSelected)) {
fcn->cc = rz_str_constpool_get(&core->analysis->constpool, ccSelected);
QByteArray newCC = dialog.getCallConSelected().toUtf8();
if (!newCC.isEmpty() && rz_analysis_cc_exist(core->analysis, newCC.constData())) {
fcn->cc = rz_str_constpool_get(&core->analysis->constpool, newCC.constData());
}
emit Core()->functionsChanged();

View File

@ -133,6 +133,10 @@ QString PluginManager::getUserPluginsDirectory() const
void PluginManager::loadNativePlugins(const QDir &directory)
{
for (const QString &fileName : directory.entryList(QDir::Files)) {
if (!QLibrary::isLibrary(fileName)) {
// Reduce amount of warnings, by not attempting files which are obviously not plugins
continue;
}
QPluginLoader pluginLoader(directory.absoluteFilePath(fileName));
QObject *plugin = pluginLoader.instance();
if (!plugin) {

View File

@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.12)
project(cutter-sample-plugin)
find_package(Cutter REQUIRED)
find_package(Rizin REQUIRED)
set(CUTTER_INSTALL_PLUGDIR "${Cutter_USER_PLUGINDIR}" CACHE STRING "Directory to install Cutter plugin into")
set(CMAKE_AUTOMOC ON)
@ -9,5 +10,5 @@ set(CMAKE_AUTOMOC ON)
add_library(sample_plugin MODULE
CutterSamplePlugin.h
CutterSamplePlugin.cpp)
target_link_libraries(sample_plugin PRIVATE Cutter::Cutter)
target_link_libraries(sample_plugin PRIVATE Cutter::Cutter Rizin::Core)
install(TARGETS sample_plugin DESTINATION "${CUTTER_INSTALL_PLUGDIR}")

View File

@ -8,6 +8,7 @@
#include <common/TempConfig.h>
#include <common/Configuration.h>
#include <MainWindow.h>
#include <librz/rz_core.h>
void CutterSamplePlugin::setupPlugin() {}
@ -46,25 +47,21 @@ CutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main) : CutterDoc
void CutterSamplePluginWidget::on_seekChanged(RVA addr)
{
Q_UNUSED(addr);
QString res;
{
TempConfig tempConfig;
tempConfig.set("scr.color", 0);
res = Core()->cmd("?E `pi 1`");
}
RzCoreLocked core(Core());
TempConfig tempConfig;
tempConfig.set("scr.color", 0);
QString disasm = Core()->disassembleSingleInstruction(Core()->getOffset());
QString res = fromOwnedCharPtr(rz_core_clippy(core, disasm.toUtf8().constData()));
text->setText(res);
}
void CutterSamplePluginWidget::on_buttonClicked()
{
RzCoreLocked core(Core());
char *fortune = rz_core_fortune_get_random(core);
auto fortune = fromOwned(rz_core_fortune_get_random(core));
if (!fortune) {
return;
}
// cmdRaw can be used to execute single raw commands
// this is especially good for user-controlled input
QString res = Core()->cmdRaw("?E " + QString::fromUtf8(fortune));
QString res = fromOwnedCharPtr(rz_core_clippy(core, fortune.get()));
text->setText(res);
rz_mem_free(fortune);
}

View File

@ -36,7 +36,7 @@ class FortuneWidget(cutter.CutterDockWidget):
self.show()
def generate_fortune(self):
fortune = cutter.cmd("fo").replace("\n", "")
fortune = cutter.cmd("fortune").replace("\n", "")
res = cutter.core().cmdRaw(f"?E {fortune}")
self.text.setText(res)
@ -44,7 +44,7 @@ class FortuneWidget(cutter.CutterDockWidget):
class CutterSamplePlugin(cutter.CutterPlugin):
name = "Sample Plugin"
description = "A sample plugin written in python."
version = "1.1"
version = "1.2"
author = "Cutter developers"
# Override CutterPlugin methods

@ -1 +1 @@
Subproject commit 974298653ba71b958e1b6c83f6011f5fefff6236
Subproject commit 41c0c778b942577749ea2fed117e48a2cf3892df

View File

@ -7,9 +7,11 @@
#include <QJsonObject>
CallGraphWidget::CallGraphWidget(MainWindow *main, bool global)
: AddressableDockWidget(main), graphView(new CallGraphView(this, main, global)), global(global)
: MemoryDockWidget(MemoryWidgetType::CallGraph, main),
graphView(new CallGraphView(this, main, global)),
global(global)
{
setObjectName(main->getUniqueObjectName("CallGraphWidget"));
setObjectName(main ? main->getUniqueObjectName(getWidgetType()) : getWidgetType());
this->setWindowTitle(getWindowTitle());
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &CallGraphWidget::onSeekChanged);
@ -23,6 +25,11 @@ QString CallGraphWidget::getWindowTitle() const
return global ? tr("Global Callgraph") : tr("Callgraph");
}
QString CallGraphWidget::getWidgetType() const
{
return global ? tr("GlobalCallgraph") : tr("Callgraph");
}
void CallGraphWidget::onSeekChanged(RVA address)
{
if (auto function = Core()->functionIn(address)) {
@ -37,6 +44,7 @@ CallGraphView::CallGraphView(CutterDockWidget *parent, MainWindow *main, bool gl
refreshDeferrer.registerFor(parent);
connect(&refreshDeferrer, &RefreshDeferrer::refreshNow, this, &CallGraphView::refreshView);
connect(Core(), &CutterCore::refreshAll, this, &SimpleTextGraphView::refreshView);
connect(Core(), &CutterCore::functionRenamed, this, &CallGraphView::refreshView);
}
void CallGraphView::showExportDialog()
@ -47,17 +55,14 @@ void CallGraphView::showExportDialog()
} else {
defaultName = QString("callgraph_%1").arg(RzAddressString(address));
}
showExportGraphDialog(defaultName, global ? "agC" : "agc", address);
showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_FUNCALL, global ? RVA_INVALID : address);
}
void CallGraphView::showAddress(RVA address)
{
if (global) {
auto addressMappingIt = addressMapping.find(address);
if (addressMappingIt != addressMapping.end()) {
selectBlockWithId(addressMappingIt->second);
showBlock(blocks[addressMappingIt->second]);
}
selectBlockWithId(address);
showBlock(blocks[address]);
} else if (address != this->address) {
this->address = address;
refreshView();
@ -72,53 +77,70 @@ void CallGraphView::refreshView()
SimpleTextGraphView::refreshView();
}
static inline bool isBetween(ut64 a, ut64 x, ut64 b)
{
return (a == UT64_MAX || a <= x) && (b == UT64_MAX || x <= b);
}
void CallGraphView::loadCurrentGraph()
{
blockContent.clear();
blocks.clear();
CutterJson nodes = Core()->cmdj(global ? "agCj" : QString("agcj @ %1").arg(address));
const ut64 from = Core()->getConfigi("graph.from");
const ut64 to = Core()->getConfigi("graph.to");
const bool usenames = Core()->getConfigb("graph.json.usenames");
QHash<QString, uint64_t> idMapping;
auto edges = std::unordered_set<ut64> {};
auto addFunction = [&](RzAnalysisFunction *fcn) {
GraphLayout::GraphBlock block;
block.entry = fcn->addr;
auto getId = [&](const QString &name) -> uint64_t {
auto nextId = idMapping.size();
auto &itemId = idMapping[name];
if (idMapping.size() != nextId) {
itemId = nextId;
auto xrefs = fromOwned(rz_analysis_function_get_xrefs_from(fcn));
auto calls = std::unordered_set<ut64>();
for (const auto &xref : CutterRzList<RzAnalysisXRef>(xrefs.get())) {
const auto x = xref->to;
if (!(xref->type == RZ_ANALYSIS_XREF_TYPE_CALL && calls.find(x) == calls.end())) {
continue;
}
calls.insert(x);
block.edges.emplace_back(x);
edges.insert(x);
}
return itemId;
QString name = usenames ? fcn->name : RzAddressString(fcn->addr);
addBlock(std::move(block), name, fcn->addr);
};
for (CutterJson block : nodes) {
QString name = block["name"].toString();
auto edges = block["imports"];
GraphLayout::GraphBlock layoutBlock;
layoutBlock.entry = getId(name);
for (auto edge : edges) {
auto targetName = edge.toString();
auto targetId = getId(targetName);
layoutBlock.edges.emplace_back(targetId);
if (global) {
for (const auto &fcn : CutterRzList<RzAnalysisFunction>(Core()->core()->analysis->fcns)) {
if (!isBetween(from, fcn->addr, to)) {
continue;
}
addFunction(fcn);
}
} else {
const auto &fcn = Core()->functionIn(address);
if (fcn) {
addFunction(fcn);
}
// it would be good if address came directly from json instead of having to lookup by name
addBlock(std::move(layoutBlock), name, Core()->num(name));
}
for (auto it = idMapping.begin(), end = idMapping.end(); it != end; ++it) {
if (blocks.find(it.value()) == blocks.end()) {
GraphLayout::GraphBlock block;
block.entry = it.value();
addBlock(std::move(block), it.key(), Core()->num(it.key()));
for (const auto &x : edges) {
if (blockContent.find(x) != blockContent.end()) {
continue;
}
GraphLayout::GraphBlock block;
block.entry = x;
QString flagName = Core()->flagAt(x);
QString name = usenames
? (!flagName.isEmpty() ? flagName : QString("unk.%0").arg(RzAddressString(x)))
: RzAddressString(x);
addBlock(std::move(block), name, x);
}
if (blockContent.empty() && !global) {
addBlock({}, RzAddressString(address), address);
}
addressMapping.clear();
for (auto &it : blockContent) {
addressMapping[it.second.address] = it.first;
const auto name = RzAddressString(address);
addBlock({}, name, address);
}
computeGraphPlacement();

View File

@ -2,7 +2,7 @@
#define CALL_GRAPH_WIDGET_H
#include "core/Cutter.h"
#include "AddressableDockWidget.h"
#include "MemoryDockWidget.h"
#include "widgets/SimpleTextGraphView.h"
#include "common/RefreshDeferrer.h"
@ -22,7 +22,6 @@ public:
protected:
bool global; ///< is this a global or function callgraph
RVA address = RVA_INVALID; ///< function address if this is not a global callgraph
std::unordered_map<RVA, ut64> addressMapping; ///< mapping from addresses to block id
void loadCurrentGraph() override;
void restoreCurrentBlock() override;
@ -31,7 +30,7 @@ private:
RVA lastLoadedAddress = RVA_INVALID;
};
class CallGraphWidget : public AddressableDockWidget
class CallGraphWidget : public MemoryDockWidget
{
Q_OBJECT
@ -39,6 +38,8 @@ public:
explicit CallGraphWidget(MainWindow *main, bool global);
~CallGraphWidget();
QString getWidgetType() const;
protected:
QString getWindowTitle() const override;

View File

@ -48,8 +48,9 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
ColorOption currCO = index.data(Qt::UserRole).value<ColorOption>();
QFontMetrics fm = QFontMetrics(painter->font());
int penWidth = painter->pen().width();
int fontHeight = painter->fontMetrics().height();
int fontHeight = fm.height();
QPoint tl = option.rect.topLeft();
QRect optionNameRect;
@ -126,9 +127,9 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
painter->setPen(qApp->palette().text().color());
QString name =
painter->fontMetrics().elidedText(optionInfoMap__[currCO.optionName].displayingtext,
Qt::ElideRight, optionNameRect.width());
QFontMetrics fm2 = QFontMetrics(painter->font());
QString name = fm2.elidedText(optionInfoMap__[currCO.optionName].displayingtext,
Qt::ElideRight, optionNameRect.width());
painter->drawText(optionNameRect, name);
QPainterPath roundedOptionRect;
@ -155,7 +156,8 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
painter->setPen(currCO.color);
painter->fillPath(roundedColorRect, currCO.color);
QString desc = painter->fontMetrics().elidedText(
QFontMetrics fm3 = QFontMetrics(painter->font());
QString desc = fm3.elidedText(
currCO.optionName + ": " + optionInfoMap__[currCO.optionName].info, Qt::ElideRight,
descTextRect.width());
painter->setPen(qApp->palette().text().color());
@ -197,7 +199,8 @@ QPixmap ColorOptionDelegate::getPixmapFromSvg(const QString &fileName, const QCo
data.replace(QRegularExpression("#[0-9a-fA-F]{6}"), QString("%1").arg(after.name()));
QSvgRenderer svgRenderer(data.toUtf8());
QPixmap pix(QSize(qApp->fontMetrics().height(), qApp->fontMetrics().height()));
QFontMetrics fm = QFontMetrics(qApp->font());
QPixmap pix(QSize(fm.height(), fm.height()));
pix.fill(Qt::transparent);
QPainter pixPainter(&pix);

View File

@ -6,8 +6,14 @@ ComboQuickFilterView::ComboQuickFilterView(QWidget *parent)
{
ui->setupUi(this);
debounceTimer = new QTimer(this);
debounceTimer->setSingleShot(true);
connect(debounceTimer, &QTimer::timeout, this,
[this]() { emit filterTextChanged(ui->lineEdit->text()); });
connect(ui->lineEdit, &QLineEdit::textChanged, this,
[this](const QString &text) { emit filterTextChanged(text); });
[this](const QString &text) { debounceTimer->start(150); });
}
ComboQuickFilterView::~ComboQuickFilterView()

View File

@ -5,6 +5,7 @@
#include <QWidget>
#include <QComboBox>
#include <QTimer>
namespace Ui {
class ComboQuickFilterView;
@ -32,6 +33,7 @@ signals:
private:
Ui::ComboQuickFilterView *ui;
QTimer *debounceTimer;
};
#endif // COMBOQUICKFILTERVIEW_H

View File

@ -152,7 +152,7 @@ void CutterGraphView::zoomReset()
void CutterGraphView::showExportDialog()
{
showExportGraphDialog("graph", "", RVA_INVALID);
showExportGraphDialog("global_funcall", RZ_CORE_GRAPH_TYPE_FUNCALL, RVA_INVALID);
}
void CutterGraphView::updateColors()
@ -318,12 +318,12 @@ void CutterGraphView::mouseMoveEvent(QMouseEvent *event)
emit graphMoved();
}
void CutterGraphView::exportGraph(QString filePath, GraphExportType type, QString graphCommand,
RVA address)
void CutterGraphView::exportGraph(QString filePath, GraphExportType exportType,
RzCoreGraphType graphType, RVA address)
{
bool graphTransparent = Config()->getBitmapTransparentState();
double graphScaleFactor = Config()->getBitmapExportScaleFactor();
switch (type) {
switch (exportType) {
case GraphExportType::Png:
this->saveAsBitmap(filePath, "png", graphScaleFactor, graphTransparent);
break;
@ -335,56 +335,55 @@ void CutterGraphView::exportGraph(QString filePath, GraphExportType type, QStrin
break;
case GraphExportType::GVDot:
exportRzTextGraph(filePath, graphCommand + "d", address);
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_DOT, address);
break;
case GraphExportType::RzJson:
exportRzTextGraph(filePath, graphCommand + "j", address);
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_JSON, address);
break;
case GraphExportType::RzGml:
exportRzTextGraph(filePath, graphCommand + "g", address);
break;
case GraphExportType::RzSDBKeyValue:
exportRzTextGraph(filePath, graphCommand + "k", address);
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_GML, address);
break;
case GraphExportType::GVJson:
exportRizinGraphvizGraph(filePath, "json", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "json", graphType, address);
break;
case GraphExportType::GVGif:
exportRizinGraphvizGraph(filePath, "gif", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "gif", graphType, address);
break;
case GraphExportType::GVPng:
exportRizinGraphvizGraph(filePath, "png", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "png", graphType, address);
break;
case GraphExportType::GVJpeg:
exportRizinGraphvizGraph(filePath, "jpg", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "jpg", graphType, address);
break;
case GraphExportType::GVPostScript:
exportRizinGraphvizGraph(filePath, "ps", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "ps", graphType, address);
break;
case GraphExportType::GVSvg:
exportRizinGraphvizGraph(filePath, "svg", graphCommand, address);
Core()->writeGraphvizGraphToFile(filePath, "svg", graphType, address);
break;
case GraphExportType::GVPdf:
Core()->writeGraphvizGraphToFile(filePath, "pdf", graphType, address);
break;
}
}
void CutterGraphView::exportRizinGraphvizGraph(QString filePath, QString type, QString graphCommand,
RVA address)
void CutterGraphView::exportRzTextGraph(QString filePath, RzCoreGraphType type,
RzCoreGraphFormat format, RVA address)
{
TempConfig tempConfig;
tempConfig.set("graph.gv.format", type);
qWarning() << Core()->cmdRawAt(QString("%0w \"%1\"").arg(graphCommand).arg(filePath), address);
}
void CutterGraphView::exportRzTextGraph(QString filePath, QString graphCommand, RVA address)
{
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "Can't open file";
char *string = Core()->getTextualGraphAt(type, format, address);
if (!string) {
return;
}
QTextStream fileOut(&file);
fileOut << Core()->cmdRawAt(QString("%0").arg(graphCommand), address);
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream fileOut(&file);
fileOut << string;
} else {
qWarning() << "Can't open or create file: " << filePath;
}
free(string);
}
bool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)
@ -403,40 +402,39 @@ bool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)
Q_DECLARE_METATYPE(CutterGraphView::GraphExportType);
void CutterGraphView::showExportGraphDialog(QString defaultName, QString graphCommand, RVA address)
void CutterGraphView::showExportGraphDialog(QString defaultName, RzCoreGraphType type, RVA address)
{
qWarning() << defaultName << " - " << type << " addr " << RzAddressString(address);
QVector<MultitypeFileSaveDialog::TypeDescription> types = {
{ tr("PNG (*.png)"), "png", QVariant::fromValue(GraphExportType::Png) },
{ tr("JPEG (*.jpg)"), "jpg", QVariant::fromValue(GraphExportType::Jpeg) },
{ tr("SVG (*.svg)"), "svg", QVariant::fromValue(GraphExportType::Svg) }
};
bool rzGraphExports = !graphCommand.isEmpty();
if (rzGraphExports) {
types.append({
{ tr("Graphviz dot (*.dot)"), "dot", QVariant::fromValue(GraphExportType::GVDot) },
{ tr("Graph Modelling Language (*.gml)"), "gml",
QVariant::fromValue(GraphExportType::RzGml) },
{ tr("RZ JSON (*.json)"), "json", QVariant::fromValue(GraphExportType::RzJson) },
{ tr("SDB key-value (*.txt)"), "txt",
QVariant::fromValue(GraphExportType::RzSDBKeyValue) },
});
bool hasGraphviz = !QStandardPaths::findExecutable("dot").isEmpty()
|| !QStandardPaths::findExecutable("xdot").isEmpty();
if (hasGraphviz) {
types.append({ { tr("Graphviz json (*.json)"), "json",
QVariant::fromValue(GraphExportType::GVJson) },
{ tr("Graphviz gif (*.gif)"), "gif",
QVariant::fromValue(GraphExportType::GVGif) },
{ tr("Graphviz png (*.png)"), "png",
QVariant::fromValue(GraphExportType::GVPng) },
{ tr("Graphviz jpg (*.jpg)"), "jpg",
QVariant::fromValue(GraphExportType::GVJpeg) },
{ tr("Graphviz PostScript (*.ps)"), "ps",
QVariant::fromValue(GraphExportType::GVPostScript) },
{ tr("Graphviz svg (*.svg)"), "svg",
QVariant::fromValue(GraphExportType::GVSvg) } });
}
types.append({
{ tr("Graphviz dot (*.dot)"), "dot", QVariant::fromValue(GraphExportType::GVDot) },
{ tr("Graph Modelling Language (*.gml)"), "gml",
QVariant::fromValue(GraphExportType::RzGml) },
{ tr("RZ JSON (*.json)"), "json", QVariant::fromValue(GraphExportType::RzJson) },
});
bool hasGraphviz = !QStandardPaths::findExecutable("dot").isEmpty()
|| !QStandardPaths::findExecutable("xdot").isEmpty();
if (hasGraphviz) {
types.append({ { tr("Graphviz json (*.json)"), "json",
QVariant::fromValue(GraphExportType::GVJson) },
{ tr("Graphviz gif (*.gif)"), "gif",
QVariant::fromValue(GraphExportType::GVGif) },
{ tr("Graphviz png (*.png)"), "png",
QVariant::fromValue(GraphExportType::GVPng) },
{ tr("Graphviz jpg (*.jpg)"), "jpg",
QVariant::fromValue(GraphExportType::GVJpeg) },
{ tr("Graphviz PostScript (*.ps)"), "ps",
QVariant::fromValue(GraphExportType::GVPostScript) },
{ tr("Graphviz svg (*.svg)"), "svg",
QVariant::fromValue(GraphExportType::GVSvg) },
{ tr("Graphviz pdf (*.pdf)"), "pdf",
QVariant::fromValue(GraphExportType::GVPdf) } });
}
MultitypeFileSaveDialog dialog(this, tr("Export Graph"));
@ -470,5 +468,5 @@ void CutterGraphView::showExportGraphDialog(QString defaultName, QString graphCo
}
QString filePath = dialog.selectedFiles().first();
exportGraph(filePath, exportType, graphCommand, address);
exportGraph(filePath, exportType, type, address);
}

View File

@ -32,48 +32,38 @@ public:
GVJpeg,
GVPostScript,
GVSvg,
GVPdf,
RzGml,
RzSDBKeyValue,
RzJson
};
/**
* @brief Export graph to a file in the specified format
* @param filePath
* @param type export type, GV* and Rz* types require \p graphCommand
* @param graphCommand rizin graph printing command without type, not required for direct image
* export
* @param address object address for commands like agf
* @param filePath - output file path
* @param exportType - export type, GV* and Rz* types require \p graphCommand
* @param graphType - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or
* RZ_CORE_GRAPH_TYPE_IMPORT
* @param address - object address (if global set it to RVA_INVALID)
*/
void exportGraph(QString filePath, GraphExportType type, QString graphCommand = "",
void exportGraph(QString filePath, GraphExportType exportType, RzCoreGraphType graphType,
RVA address = RVA_INVALID);
/**
* @brief Export image using rizin ag*w command and graphviz.
* Requires graphviz dot executable in the path.
*
* @param filePath output file path
* @param type image format as expected by "e graph.gv.format"
* @param graphCommand rizin command without type, for example agf
* @param address object address if required by command
*/
void exportRizinGraphvizGraph(QString filePath, QString type, QString graphCommand,
RVA address);
/**
* @brief Export graph in one of the text formats supported by rizin json, gml, SDB key-value
* @param filePath output file path
* @param graphCommand graph command including the format, example "agfd" or "agfg"
* @param address object address if required by command
* @param filePath - output file path
* @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
* @param format - graph format, example RZ_CORE_GRAPH_FORMAT_DOT or RZ_CORE_GRAPH_FORMAT_GML
* @param address - object address (if global set it to RVA_INVALID)
*/
void exportRzTextGraph(QString filePath, QString graphCommand, RVA address);
void exportRzTextGraph(QString filePath, RzCoreGraphType type, RzCoreGraphFormat format,
RVA address);
static bool graphIsBitamp(GraphExportType type);
/**
* @brief Show graph export dialog.
* @param defaultName - default file name in the export dialog
* @param graphCommand - rizin graph commmand with graph type and without export type, for
* example afC. Leave empty for non-rizin graphs. In such case only direct image export will be
* available.
* @param address - object address if relevant for \p graphCommand
* @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
* @param address - object address (if global set it to RVA_INVALID)
*/
void showExportGraphDialog(QString defaultName, QString graphCommand = "",
void showExportGraphDialog(QString defaultName, RzCoreGraphType type,
RVA address = RVA_INVALID);
public slots:

View File

@ -4,8 +4,13 @@
CutterTreeView::CutterTreeView(QWidget *parent) : QTreeView(parent), ui(new Ui::CutterTreeView())
{
ui->setupUi(this);
this->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->setUniformRowHeights(true);
applyCutterStyle(this);
}
CutterTreeView::~CutterTreeView() {}
void CutterTreeView::applyCutterStyle(QTreeView *view)
{
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
view->setUniformRowHeights(true);
}

View File

@ -19,6 +19,8 @@ public:
explicit CutterTreeView(QWidget *parent = nullptr);
~CutterTreeView();
static void applyCutterStyle(QTreeView *view);
private:
std::unique_ptr<Ui::CutterTreeView> ui;
};

View File

@ -2,7 +2,6 @@
#include "ui_Dashboard.h"
#include "common/Helpers.h"
#include "common/JsonModel.h"
#include "common/JsonTreeItem.h"
#include "common/TempConfig.h"
#include "dialogs/VersionInfoDialog.h"
@ -32,24 +31,27 @@ Dashboard::~Dashboard() {}
void Dashboard::updateContents()
{
CutterJson docu = Core()->getFileInfo();
CutterJson item = docu["core"];
CutterJson item2 = docu["bin"];
RzCoreLocked core(Core());
int fd = rz_io_fd_get_current(core->io);
RzIODesc *desc = rz_io_desc_get(core->io, fd);
setPlainText(this->ui->modeEdit, desc ? rz_str_rwx_i(desc->perm & RZ_PERM_RWX) : "");
setPlainText(this->ui->modeEdit, item["mode"].toString());
setPlainText(this->ui->compilationDateEdit, item2["compiled"].toString());
if (!item2["relro"].toString().isEmpty()) {
QString relro = item2["relro"].toString().section(QLatin1Char(' '), 0, 0);
relro[0] = relro[0].toUpper();
setPlainText(this->ui->relroEdit, relro);
} else {
setPlainText(this->ui->relroEdit, "N/A");
RzBinFile *bf = rz_bin_cur(core->bin);
if (bf) {
setPlainText(this->ui->compilationDateEdit, rz_core_bin_get_compile_time(bf));
if (bf->o) {
char *relco_buf = sdb_get(bf->o->kv, "elf.relro", 0);
if (RZ_STR_ISNOTEMPTY(relco_buf)) {
QString relro = QString(relco_buf).section(QLatin1Char(' '), 0, 0);
relro[0] = relro[0].toUpper();
setPlainText(this->ui->relroEdit, relro);
} else {
setPlainText(this->ui->relroEdit, "N/A");
}
}
}
// Add file hashes, analysis info and libraries
RzCoreLocked core(Core());
RzBinFile *bf = rz_bin_cur(core->bin);
RzBinInfo *binInfo = rz_bin_get_info(core->bin);
setPlainText(ui->fileEdit, binInfo ? binInfo->file : "");
@ -111,82 +113,68 @@ void Dashboard::updateContents()
hashesLayout->addRow(new QLabel(label), hashLineEdit);
}
CutterJson analinfo = Core()->cmdj("aaij");
setPlainText(ui->functionsLineEdit, QString::number(analinfo["fcns"].toSt64()));
setPlainText(ui->xRefsLineEdit, QString::number(analinfo["xrefs"].toSt64()));
setPlainText(ui->callsLineEdit, QString::number(analinfo["calls"].toSt64()));
setPlainText(ui->stringsLineEdit, QString::number(analinfo["strings"].toSt64()));
setPlainText(ui->symbolsLineEdit, QString::number(analinfo["symbols"].toSt64()));
setPlainText(ui->importsLineEdit, QString::number(analinfo["imports"].toSt64()));
setPlainText(ui->coverageLineEdit, QString::number(analinfo["covrage"].toSt64()) + " bytes");
setPlainText(ui->codeSizeLineEdit, QString::number(analinfo["codesz"].toSt64()) + " bytes");
setPlainText(ui->percentageLineEdit, QString::number(analinfo["percent"].toSt64()) + "%");
st64 fcns = rz_list_length(core->analysis->fcns);
st64 strs = rz_flag_count(core->flags, "str.*");
st64 syms = rz_flag_count(core->flags, "sym.*");
st64 imps = rz_flag_count(core->flags, "sym.imp.*");
st64 code = rz_core_analysis_code_count(core);
st64 covr = rz_core_analysis_coverage_count(core);
st64 call = rz_core_analysis_calls_count(core);
ut64 xrfs = rz_analysis_xrefs_count(core->analysis);
double precentage = (code > 0) ? (covr * 100.0 / code) : 0;
// dunno: why not label->setText(lines.join("\n")?
while (ui->verticalLayout_2->count() > 0) {
QLayoutItem *item = ui->verticalLayout_2->takeAt(0);
if (item != nullptr) {
QWidget *w = item->widget();
if (w != nullptr) {
w->deleteLater();
}
delete item;
}
}
setPlainText(ui->functionsLineEdit, QString::number(fcns));
setPlainText(ui->xRefsLineEdit, QString::number(xrfs));
setPlainText(ui->callsLineEdit, QString::number(call));
setPlainText(ui->stringsLineEdit, QString::number(strs));
setPlainText(ui->symbolsLineEdit, QString::number(syms));
setPlainText(ui->importsLineEdit, QString::number(imps));
setPlainText(ui->coverageLineEdit, QString::number(covr) + " bytes");
setPlainText(ui->codeSizeLineEdit, QString::number(code) + " bytes");
setPlainText(ui->percentageLineEdit, QString::number(precentage) + "%");
ui->libraryList->setPlainText("");
const RzList *libs = bf ? rz_bin_object_get_libs(bf->o) : nullptr;
if (libs) {
QString libText;
bool first = true;
for (const auto &lib : CutterRzList<char>(libs)) {
auto *label = new QLabel(this);
label->setText(lib);
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
ui->verticalLayout_2->addWidget(label);
if (!first) {
libText.append("\n");
}
libText.append(lib);
first = false;
}
ui->libraryList->setPlainText(libText);
}
QSpacerItem *spacer = new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding);
ui->verticalLayout_2->addSpacerItem(spacer);
// Check if signature info and version info available
if (Core()->getSignatureInfo().isEmpty()) {
if (!Core()->getSignatureInfo().size()) {
ui->certificateButton->setEnabled(false);
}
if (!Core()->getFileVersionInfo().size()) {
ui->versioninfoButton->setEnabled(false);
}
ui->versioninfoButton->setEnabled(Core()->existsFileInfo());
}
void Dashboard::on_certificateButton_clicked()
{
static QDialog *viewDialog = nullptr;
static CutterTreeView *view = nullptr;
static JsonModel *model = nullptr;
static QString qstrCertificates;
if (!viewDialog) {
viewDialog = new QDialog(this);
view = new CutterTreeView(viewDialog);
model = new JsonModel();
qstrCertificates = Core()->getSignatureInfo();
}
if (!viewDialog->isVisible()) {
std::string strCertificates = qstrCertificates.toUtf8().constData();
model->loadJson(QByteArray::fromStdString(strCertificates));
view->setModel(model);
view->expandAll();
view->resize(900, 600);
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(view->sizePolicy().hasHeightForWidth());
viewDialog->setSizePolicy(sizePolicy);
viewDialog->setMinimumSize(QSize(900, 600));
viewDialog->setMaximumSize(QSize(900, 600));
viewDialog->setSizeGripEnabled(false);
viewDialog->setWindowTitle("Certificates");
viewDialog->show();
}
QDialog dialog(this);
auto view = new QTreeWidget(&dialog);
view->setHeaderLabels({ tr("Key"), tr("Value") });
view->addTopLevelItem(Cutter::jsonTreeWidgetItem(QString("<%1>").arg(tr("root")),
Core()->getSignatureInfo()));
CutterTreeView::applyCutterStyle(view);
view->expandAll();
view->resize(900, 600);
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(view->sizePolicy().hasHeightForWidth());
dialog.setSizePolicy(sizePolicy);
dialog.setMinimumSize(QSize(900, 600));
dialog.setMaximumSize(QSize(900, 600));
dialog.setSizeGripEnabled(false);
dialog.setWindowTitle("Certificates");
dialog.exec();
}
void Dashboard::on_versioninfoButton_clicked()

View File

@ -42,7 +42,7 @@
<enum>QFrame::Plain</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
@ -1331,10 +1331,33 @@
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
<widget class="QPlainTextEdit" name="libraryList">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>

View File

@ -109,14 +109,14 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
if (c.isValid()) {
bbh->highlight(currBlockEntry, c);
}
Config()->colorsUpdated();
emit Config()->colorsUpdated();
});
actionUnhighlight.setText(tr("Unhighlight block"));
connect(&actionUnhighlight, &QAction::triggered, this, [this]() {
auto bbh = Core()->getBBHighlighter();
bbh->clear(blockForAddress(this->seekable->getOffset())->entry);
Config()->colorsUpdated();
emit Config()->colorsUpdated();
});
QAction *highlightBI = new QAction(this);
@ -162,15 +162,15 @@ void DisassemblerGraphView::connectSeekChanged(bool disconn)
DisassemblerGraphView::~DisassemblerGraphView()
{
for (QShortcut *shortcut : shortcuts) {
delete shortcut;
}
qDeleteAll(shortcuts);
shortcuts.clear();
}
void DisassemblerGraphView::refreshView()
{
CutterGraphView::refreshView();
loadCurrentGraph();
breakpoints = Core()->getBreakpointsAddresses();
emit viewRefreshed();
}
@ -182,13 +182,6 @@ void DisassemblerGraphView::loadCurrentGraph()
.set("asm.lines", false)
.set("asm.lines.fcn", false);
CutterJson functions;
RzAnalysisFunction *fcn = Core()->functionIn(seekable->getOffset());
if (fcn) {
currentFcnAddr = fcn->addr;
functions = Core()->cmdj("agJ " + RzAddressString(fcn->addr));
}
disassembly_blocks.clear();
blocks.clear();
@ -197,7 +190,19 @@ void DisassemblerGraphView::loadCurrentGraph()
highlight_token = nullptr;
}
emptyGraph = !functions.size();
RzAnalysisFunction *fcn = Core()->functionIn(seekable->getOffset());
windowTitle = tr("Graph");
if (fcn && RZ_STR_ISNOTEMPTY(fcn->name)) {
currentFcnAddr = fcn->addr;
auto fcnName = fromOwned(rz_str_escape_utf8_for_json(fcn->name, -1));
windowTitle += QString("(%0)").arg(fcnName.get());
} else {
windowTitle += "(Empty)";
}
emit nameChanged(windowTitle);
emptyGraph = !fcn;
if (emptyGraph) {
// If there's no function to print, just add a message
if (!emptyText) {
@ -213,31 +218,20 @@ void DisassemblerGraphView::loadCurrentGraph()
}
// Refresh global "empty graph" variable so other widget know there is nothing to show here
Core()->setGraphEmpty(emptyGraph);
setEntry(fcn ? fcn->addr : RVA_INVALID);
CutterJson func = functions.first();
windowTitle = tr("Graph");
QString funcName = func["name"].toString().trimmed();
if (emptyGraph) {
windowTitle += " (Empty)";
} else if (!funcName.isEmpty()) {
windowTitle += " (" + funcName + ")";
if (!fcn) {
return;
}
emit nameChanged(windowTitle);
RVA entry = func["offset"].toRVA();
setEntry(entry);
for (CutterJson block : func["blocks"]) {
RVA block_entry = block["offset"].toRVA();
RVA block_size = block["size"].toRVA();
RVA block_fail = block["fail"].toRVA();
RVA block_jump = block["jump"].toRVA();
for (const auto &bbi : CutterRzList<RzAnalysisBlock>(fcn->bbs)) {
RVA bbiFail = bbi->fail;
RVA bbiJump = bbi->jump;
DisassemblyBlock db;
GraphBlock gb;
gb.entry = block_entry;
db.entry = block_entry;
gb.entry = bbi->addr;
db.entry = bbi->addr;
if (Config()->getGraphBlockEntryOffset()) {
// QColor(0,0,0,0) is transparent
db.header_text = Text("[" + RzAddressString(db.entry) + "]", ConfigColor("offset"),
@ -245,50 +239,67 @@ void DisassemblerGraphView::loadCurrentGraph()
}
db.true_path = RVA_INVALID;
db.false_path = RVA_INVALID;
if (block_fail) {
db.false_path = block_fail;
gb.edges.emplace_back(block_fail);
if (bbiFail) {
db.false_path = bbiFail;
gb.edges.emplace_back(bbiFail);
}
if (block_jump) {
if (block_fail) {
db.true_path = block_jump;
if (bbiJump) {
if (bbiFail) {
db.true_path = bbiJump;
}
gb.edges.emplace_back(block_jump);
gb.edges.emplace_back(bbiJump);
}
CutterJson switchOp = block["switchop"];
if (switchOp.size()) {
for (CutterJson caseOp : switchOp["cases"]) {
RVA caseJump = caseOp["jump"].toRVA();
if (caseJump == RVA_INVALID) {
RzAnalysisSwitchOp *switchOp = bbi->switch_op;
if (switchOp) {
for (const auto &caseOp : CutterRzList<RzAnalysisCaseOp>(switchOp->cases)) {
if (caseOp->jump == RVA_INVALID) {
continue;
}
gb.edges.emplace_back(caseJump);
gb.edges.emplace_back(caseOp->jump);
}
}
CutterJson opArray = block["ops"];
CutterJson::iterator iterator = opArray.begin();
while (iterator != opArray.end()) {
CutterJson op = *iterator;
Instr i;
i.addr = op["offset"].toUt64();
RzCoreLocked core(Core());
std::unique_ptr<ut8[]> buf { new ut8[bbi->size] };
if (!buf) {
break;
}
rz_io_read_at(core->io, bbi->addr, buf.get(), (int)bbi->size);
++iterator;
auto vec = fromOwned(
rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));
if (!vec) {
break;
}
if (iterator != opArray.end()) {
RzCoreDisasmOptions options = {};
options.vec = vec.get();
options.cbytes = 1;
rz_core_print_disasm(core, bbi->addr, buf.get(), (int)bbi->size, (int)bbi->size, NULL,
&options);
auto vecVisitor = CutterPVector<RzAnalysisDisasmText>(vec.get());
auto iter = vecVisitor.begin();
while (iter != vecVisitor.end()) {
RzAnalysisDisasmText *op = *iter;
Instr instr;
instr.addr = op->offset;
++iter;
if (iter != vecVisitor.end()) {
// get instruction size from distance to next instruction ...
RVA nextOffset = (*iterator)["offset"].toRVA();
i.size = nextOffset - i.addr;
RVA nextOffset = (*iter)->offset;
instr.size = nextOffset - instr.addr;
} else {
// or to the end of the block.
i.size = (block_entry + block_size) - i.addr;
instr.size = (bbi->addr + bbi->size) - instr.addr;
}
QTextDocument textDoc;
textDoc.setHtml(CutterCore::ansiEscapeToHtml(op["text"].toString()));
textDoc.setHtml(CutterCore::ansiEscapeToHtml(op->text));
i.plainText = textDoc.toPlainText();
instr.plainText = textDoc.toPlainText();
RichTextPainter::List richText = RichTextPainter::fromTextDocument(textDoc);
// Colors::colorizeAssembly(richText, textDoc.toPlainText(), 0);
@ -296,23 +307,19 @@ void DisassemblerGraphView::loadCurrentGraph()
bool cropped;
int blockLength = Config()->getGraphBlockMaxChars()
+ Core()->getConfigb("asm.bytes") * 24 + Core()->getConfigb("asm.emu") * 10;
i.text = Text(RichTextPainter::cropped(richText, blockLength, "...", &cropped));
instr.text = Text(RichTextPainter::cropped(richText, blockLength, "...", &cropped));
if (cropped)
i.fullText = richText;
instr.fullText = richText;
else
i.fullText = Text();
db.instrs.push_back(i);
instr.fullText = Text();
db.instrs.push_back(instr);
}
disassembly_blocks[db.entry] = db;
prepareGraphNode(gb);
addBlock(gb);
}
cleanupEdges(blocks);
if (func["blocks"].size()) {
computeGraphPlacement();
}
computeGraphPlacement();
}
DisassemblerGraphView::EdgeConfigurationMapping DisassemblerGraphView::getEdgeConfigurations()
@ -365,8 +372,6 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block,
p.setFont(Config()->getFont());
p.drawRect(blockRect);
breakpoints = Core()->getBreakpointsAddresses();
// Render node
DisassemblyBlock &db = disassembly_blocks[block.entry];
bool block_selected = false;
@ -887,6 +892,11 @@ void DisassemblerGraphView::contextMenuEvent(QContextMenuEvent *event)
void DisassemblerGraphView::showExportDialog()
{
if (currentFcnAddr == RVA_INVALID) {
qWarning() << "Cannot find current function.";
return;
}
QString defaultName = "graph";
if (auto f = Core()->functionIn(currentFcnAddr)) {
QString functionName = f->name;
@ -897,7 +907,7 @@ void DisassemblerGraphView::showExportDialog()
defaultName = functionName;
}
}
showExportGraphDialog(defaultName, "agf", currentFcnAddr);
showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_BLOCK_FUN, currentFcnAddr);
}
void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event,

View File

@ -211,7 +211,7 @@ QString DisassemblyWidget::getWidgetType()
QFontMetrics DisassemblyWidget::getFontMetrics()
{
return mDisasTextEdit->fontMetrics();
return QFontMetrics(mDisasTextEdit->font());
}
QList<DisassemblyLine> DisassemblyWidget::getLines()

View File

@ -231,7 +231,15 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
QStringList disasmPreview =
Core()->getDisassemblyPreview(function.offset, kMaxTooltipDisasmPreviewLines);
const QStringList &summary = Core()->cmdList(QString("pdsf @ %1").arg(function.offset));
QStringList summary {};
{
auto seeker = Core()->seekTemp(function.offset);
auto strings = fromOwnedCharPtr(
rz_core_print_disasm_strings(Core()->core(), RZ_CORE_DISASM_STRINGS_MODE_FUNCTION,
0, NULL));
summary = strings.split('\n', CUTTER_QT_SKIP_EMPTY_PARTS);
}
const QFont &fnt = Config()->getFont();
QFontMetrics fm { fnt };
@ -245,7 +253,7 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
}
}
if (disasmPreview.isEmpty() && highlights.isEmpty())
return QVariant();
return {};
QString toolTipContent =
QString("<html><div style=\"font-family: %1; font-size: %2pt; white-space: "
@ -287,7 +295,7 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
return importAddresses->contains(function.offset);
default:
return QVariant();
return {};
}
}
@ -564,7 +572,18 @@ void FunctionsWidget::refreshTree()
importAddresses.insert(import.plt);
}
mainAdress = (ut64)Core()->cmdj("iMj")["vaddr"].toUt64();
mainAdress = RVA_INVALID;
RzCoreLocked core(Core());
RzBinFile *bf = rz_bin_cur(core->bin);
if (bf) {
const RzBinAddr *binmain =
rz_bin_object_get_special_symbol(bf->o, RZ_BIN_SPECIAL_SYMBOL_MAIN);
if (binmain) {
int va = core->io->va || core->bin->is_debugger;
mainAdress = va ? rz_bin_object_addr_with_base(bf->o, binmain->vaddr)
: binmain->paddr;
}
}
functionModel->updateCurrentIndex();
functionModel->endResetModel();

View File

@ -703,7 +703,10 @@ void GraphView::mouseDoubleClickEvent(QMouseEvent *event)
void GraphView::keyPressEvent(QKeyEvent *event)
{
// for scrolling with arrow keys
const int delta = static_cast<int>(30.0 / current_scale);
// for scrolling with pgup/pgdown keys
const int delta2 = static_cast<int>(100.0 / current_scale);
int dx = 0, dy = 0;
switch (event->key()) {
case Qt::Key_Up:
@ -718,6 +721,12 @@ void GraphView::keyPressEvent(QKeyEvent *event)
case Qt::Key_Right:
dx = delta;
break;
case Qt::Key_PageUp:
dy = -delta2;
break;
case Qt::Key_PageDown:
dy = delta2;
break;
default:
QAbstractScrollArea::keyPressEvent(event);
return;

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ struct BasicCursor
{
uint64_t address;
bool pastEnd;
BasicCursor(uint64_t pos) : address(pos), pastEnd(false) {}
explicit BasicCursor(uint64_t pos) : address(pos), pastEnd(false) {}
BasicCursor() : address(0), pastEnd(false) {}
BasicCursor &operator+=(int64_t offset)
{
@ -35,6 +35,14 @@ struct BasicCursor
*this += int64_t(offset);
return *this;
}
bool moveChecked(int offset)
{
auto oldAddress = address;
*this += offset;
return address - oldAddress == uint64_t(offset);
}
BasicCursor &operator+=(uint64_t offset)
{
if (uint64_t(offset) > (UINT64_MAX - address)) {
@ -46,7 +54,10 @@ struct BasicCursor
}
return *this;
}
bool operator<(const BasicCursor &r) { return address < r.address || (pastEnd < r.pastEnd); }
bool operator<(const BasicCursor &r) const
{
return address < r.address || (pastEnd < r.pastEnd);
}
};
struct HexCursor
@ -74,9 +85,10 @@ struct HexCursor
class AbstractData
{
public:
virtual ~AbstractData() {}
virtual ~AbstractData() = default;
virtual void fetch(uint64_t addr, int len) = 0;
virtual bool copy(void *out, uint64_t adr, size_t len) = 0;
virtual bool write(const uint8_t *in, uint64_t adr, size_t len) = 0;
virtual uint64_t maxIndex() = 0;
virtual uint64_t minIndex() = 0;
};
@ -86,7 +98,7 @@ class BufferData : public AbstractData
public:
BufferData() { m_buffer.fill(0, 1); }
BufferData(const QByteArray &buffer)
explicit BufferData(const QByteArray &buffer)
{
if (buffer.isEmpty()) {
m_buffer.fill(0, 1);
@ -95,7 +107,7 @@ public:
}
}
~BufferData() override {}
~BufferData() override = default;
void fetch(uint64_t, int) override {}
@ -109,6 +121,16 @@ public:
return false;
}
bool write(const uint8_t *in, uint64_t addr, size_t len) override
{
if (addr < static_cast<uint64_t>(m_buffer.size())
&& (static_cast<uint64_t>(m_buffer.size()) - addr) < len) {
memcpy(m_buffer.data() + addr, in, len);
return true;
}
return false;
}
uint64_t maxIndex() override { return m_buffer.size() - 1; }
private:
@ -118,8 +140,8 @@ private:
class MemoryData : public AbstractData
{
public:
MemoryData() {}
~MemoryData() override {}
MemoryData() = default;
~MemoryData() override = default;
static constexpr size_t BLOCK_SIZE = 4096;
void fetch(uint64_t address, int length) override
@ -144,10 +166,11 @@ public:
bool copy(void *out, uint64_t addr, size_t len) override
{
if (addr < m_firstBlockAddr || addr > m_lastValidAddr
|| (m_lastValidAddr - addr + 1)
< len /* do not merge with last check to handle overflows */
|| m_blocks.isEmpty()) {
if (addr < m_firstBlockAddr
|| addr > m_lastValidAddr
/* do not merge with previous check to handle overflows */
|| (m_lastValidAddr - addr + 1) < len || m_blocks.isEmpty()) {
memset(out, 0xff, len);
return false;
}
@ -165,9 +188,47 @@ public:
return true;
}
virtual uint64_t maxIndex() override { return m_lastValidAddr; }
void writeToCache(const uint8_t *in, uint64_t adr, size_t len)
{
if (adr < m_firstBlockAddr) {
uint64_t prefix = m_firstBlockAddr - adr;
if (prefix <= len) {
return;
}
in = in + prefix;
adr += prefix;
len -= prefix;
}
if (adr > m_lastValidAddr) {
return;
}
int offset = (int)(adr - m_firstBlockAddr);
int blockId = offset / BLOCK_SIZE;
int blockOffset = offset % BLOCK_SIZE;
while (len > 0 && blockId < m_blocks.size()) {
size_t l = BLOCK_SIZE - blockOffset;
l = std::min(l, len);
memcpy(m_blocks[blockId].data() + blockOffset, in, l);
len -= l;
blockOffset = 0;
adr += l;
in += l;
blockId += 1;
}
}
virtual uint64_t minIndex() override { return m_firstBlockAddr; }
bool write(const uint8_t *in, uint64_t adr, size_t len) override
{
RzCoreLocked core(Core());
rz_core_write_at(core, adr, in, len);
writeToCache(in, adr, len);
emit Core()->instructionChanged(adr);
return true;
}
uint64_t maxIndex() override { return std::numeric_limits<uint64_t>::max(); }
uint64_t minIndex() override { return m_firstBlockAddr; }
private:
QVector<QByteArray> m_blocks;
@ -178,7 +239,11 @@ private:
class HexSelection
{
public:
HexSelection() { m_empty = true; }
HexSelection()
{
m_empty = true;
m_start = m_end = 0;
}
inline void init(BasicCursor addr)
{
@ -189,7 +254,8 @@ public:
void set(uint64_t start, uint64_t end)
{
m_empty = false;
m_init = m_start = start;
m_init = BasicCursor(start);
m_start = start;
m_end = end;
}
@ -219,7 +285,7 @@ public:
bool contains(uint64_t pos) const { return !m_empty && m_start <= pos && pos <= m_end; }
uint64_t size()
uint64_t size() const
{
uint64_t size = 0;
if (!isEmpty())
@ -227,9 +293,9 @@ public:
return size;
}
inline bool isEmpty() { return m_empty; }
inline uint64_t start() { return m_start; }
inline uint64_t end() { return m_end; }
inline bool isEmpty() const { return m_empty; }
inline uint64_t start() const { return m_start; }
inline uint64_t end() const { return m_end; }
private:
BasicCursor m_init;
@ -244,7 +310,7 @@ class HexWidget : public QScrollArea
public:
explicit HexWidget(QWidget *parent = nullptr);
~HexWidget();
~HexWidget() override = default;
void setMonospaceFont(const QFont &font);
@ -258,10 +324,12 @@ public:
ItemFormatFloat
};
enum class ColumnMode { Fixed, PowerOf2 };
enum class EditWordState { Read, WriteNotStarted, WriteNotEdited, WriteEdited };
enum class HexNavigationMode { Words, WordChar, AnyChar };
void setItemSize(int nbytes);
void setItemFormat(ItemFormat format);
void setItemEndianess(bool bigEndian);
void setItemEndianness(bool bigEndian);
void setItemGroupSize(int size);
/**
* @brief Sets line size in bytes.
@ -292,7 +360,7 @@ public slots:
void refresh();
void updateColors();
signals:
void selectionChanged(Selection selection);
void selectionChanged(HexWidget::Selection selection);
void positionChanged(RVA start);
protected:
@ -300,10 +368,12 @@ protected:
void resizeEvent(QResizeEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
bool event(QEvent *event) override;
private slots:
void onCursorBlinked();
@ -311,13 +381,13 @@ private slots:
void copy();
void copyAddress();
void onRangeDialogAccepted();
void on_actionAddComment_triggered();
void on_actionDeleteComment_triggered();
void onActionAddCommentTriggered();
void onActionDeleteCommentTriggered();
// Write command slots
void w_writeString();
void w_increaseDecrease();
void w_writeBytes();
void w_writeBytes();
void w_writeZeros();
void w_write64();
void w_writeRandom();
@ -326,6 +396,9 @@ private slots:
void w_writeWideString();
void w_writeCString();
void onKeyboardEditTriggered(bool enabled);
void onKeyboardEditChanged(bool enabled);
private:
void updateItemLength();
void updateCounts();
@ -338,12 +411,15 @@ private:
void updateMetrics();
void updateAreasPosition();
void updateAreasHeight();
void moveCursor(int offset, bool select = false);
enum class OverflowMove { Clamp, Ignore };
bool moveCursor(int offset, bool select = false,
OverflowMove overflowMove = OverflowMove::Clamp);
void moveCursorKeepEditOffset(int byteOffset, bool select, OverflowMove overflowMove);
void setCursorAddr(BasicCursor addr, bool select = false);
void updateCursorMeta();
void setCursorOnAscii(bool ascii);
bool isItemDifferentAt(uint64_t address);
const QColor itemColor(uint8_t byte);
QColor itemColor(uint8_t byte);
QVariant readItem(int offset, QColor *color = nullptr);
QString renderItem(int offset, QColor *color = nullptr);
QChar renderAscii(int offset, QColor *color = nullptr);
@ -359,12 +435,13 @@ private:
/**
* @brief Convert mouse position to address.
* @param point mouse position in widget
* @param middle start next position from middle of symbol. Use middle=true for vertical cursror
* @param middle start next position from middle of symbol. Use middle=true for vertical cursor
* position between symbols, middle=false for insert mode cursor and getting symbol under
* cursor.
* @return
*/
BasicCursor screenPosToAddr(const QPoint &point, bool middle = false) const;
BasicCursor screenPosToAddr(const QPoint &point, bool middle = false,
int *wordOffset = nullptr) const;
BasicCursor asciiPosToAddr(const QPoint &point, bool middle = false) const;
BasicCursor currentAreaPosToAddr(const QPoint &point, bool middle = false) const;
BasicCursor mousePosToAddr(const QPoint &point, bool middle = false) const;
@ -412,6 +489,27 @@ private:
inline uint64_t lastVisibleAddr() const { return (startAddress - 1) + bytesPerScreen(); }
const QRectF &currentArea() const { return cursorOnAscii ? asciiArea : itemArea; }
bool isFixedWidth() const;
bool canKeyboardEdit();
bool flushCurrentlyEditedWord();
bool finishEditingWord(bool force = true);
void maybeFlushCharEdit();
void cancelEditedWord();
void startEditWord();
bool validCharForEdit(QChar digit);
void movePrevEditCharAny();
void typeOverwriteModeChar(QChar c);
HexNavigationMode defaultNavigationMode();
void refreshWordEditState();
bool parseWord(QString word, uint8_t *buf, size_t bufferSize) const;
bool handleAsciiWrite(QKeyEvent *event);
bool handleNumberWrite(QKeyEvent *event);
void writeZeros(uint64_t address, uint64_t length);
void hideWarningRect();
void showWarningRect(QRectF rect);
bool cursorEnabled;
bool cursorOnAscii;
@ -436,14 +534,13 @@ private:
ItemFormat itemFormat;
bool itemBigEndian;
QString itemPrefix;
int visibleLines;
uint64_t startAddress;
qreal charWidth;
int byteWidth;
qreal lineHeight;
int addrCharLen;
int addrAreaWidth;
QFont monospaceFont;
bool showHeader;
@ -460,6 +557,7 @@ private:
QColor b0x7fColor;
QColor b0xffColor;
QColor printableColor;
QColor warningColor;
HexdumpRangeDialog rangeDialog;
@ -479,14 +577,30 @@ private:
QAction *actionCopyAddress;
QAction *actionComment;
QAction *actionDeleteComment;
QAction *actionSetFlag;
QAction *actionSelectRange;
QAction *actionKeyboardEdit;
QList<QAction *> actionsWriteString;
QList<QAction *> actionsWriteOther;
std::unique_ptr<AbstractData> oldData;
std::unique_ptr<AbstractData> data;
IOModesController ioModesController;
int editWordPos = 0;
QString editWord;
EditWordState editWordState = EditWordState::Read;
HexNavigationMode navigationMode = HexNavigationMode::Words;
enum class EarlyEditFlush {
OnFinish,
EditNibble,
EditFixedWidthChar,
/* AllFormats(not implemented) */
};
EarlyEditFlush earlyEditFlush = EarlyEditFlush::EditFixedWidthChar;
bool warningRectVisible = false;
QRectF warningRect;
QTimer warningTimer;
};
#endif // HEXWIDGET_H

View File

@ -144,7 +144,6 @@ void HexdumpWidget::initParsing()
ui->parseTypeComboBox->addItem(tr("String"), "pcs");
ui->parseTypeComboBox->addItem(tr("Assembler"), "pca");
ui->parseTypeComboBox->addItem(tr("C bytes"), "pc");
ui->parseTypeComboBox->addItem(tr("C bytes with instructions"), "pci");
ui->parseTypeComboBox->addItem(tr("C half-words (2 byte)"), "pch");
ui->parseTypeComboBox->addItem(tr("C words (4 byte)"), "pcw");
ui->parseTypeComboBox->addItem(tr("C dwords (8 byte)"), "pcd");
@ -240,28 +239,28 @@ void HexdumpWidget::updateParseWindow(RVA start_address, int size)
ui->hexDisasTextEdit->setPlainText(
selectedCommand != "" ? Core()->cmdRawAt(
QString("%1 %2").arg(selectedCommand).arg(size), start_address)
QString("%1 @! %2").arg(selectedCommand).arg(size), start_address)
: "");
} else {
// Fill the information tab hashes and entropy
RzMsgDigestSize digest_size = 0;
RzHashSize digest_size = 0;
RzCoreLocked core(Core());
ut64 old_offset = core->offset;
rz_core_seek(core, start_address, true);
ut8 *block = core->block;
char *digest = rz_msg_digest_calculate_small_block_string("md5", block, size, &digest_size, false);
char *digest = rz_hash_cfg_calculate_small_block_string(core->hash, "md5", block, size, &digest_size, false);
ui->bytesMD5->setText(QString(digest));
free(digest);
digest = rz_msg_digest_calculate_small_block_string("sha1", block, size, &digest_size, false);
digest = rz_hash_cfg_calculate_small_block_string(core->hash, "sha1", block, size, &digest_size, false);
ui->bytesSHA1->setText(QString(digest));
free(digest);
digest = rz_msg_digest_calculate_small_block_string("sha256", block, size, &digest_size, false);
digest = rz_hash_cfg_calculate_small_block_string(core->hash, "sha256", block, size, &digest_size, false);
ui->bytesSHA256->setText(QString(digest));
free(digest);
digest = rz_msg_digest_calculate_small_block_string("crc32", block, size, &digest_size, false);
digest = rz_hash_cfg_calculate_small_block_string(core->hash, "crc32", block, size, &digest_size, false);
ui->bytesCRC32->setText(QString(digest));
free(digest);
digest = rz_msg_digest_calculate_small_block_string("entropy", block, size, &digest_size, false);
digest = rz_hash_cfg_calculate_small_block_string(core->hash, "entropy", block, size, &digest_size, false);
ui->bytesEntropy->setText(QString(digest));
free(digest);
rz_core_seek(core, old_offset, true);

View File

@ -28,6 +28,7 @@ private:
const QRegularExpression banned = QRegularExpression(
QStringLiteral("\\A(\\w\\.)*(system|strcpy|strcpyA|strcpyW|wcscpy|_tcscpy|_mbscpy|"
"StrCpy|StrCpyA|StrCpyW|lstrcpy|lstrcpyA|lstrcpyW"
"DCIEnum|DCIOpenProvider|DCISendCommand|DCIBeginAccess"
"|_tccpy|_mbccpy|_ftcscpy|strcat|strcatA|strcatW|wcscat|_tcscat|_mbscat|"
"StrCat|StrCatA|StrCatW|lstrcat|lstrcatA|"
"lstrcatW|StrCatBuff|StrCatBuffA|StrCatBuffW|StrCatChainW|_tccat|_"
@ -51,7 +52,7 @@ private:
"ui64tow|_ultoa|_ultot|_ultow|CharToOem|CharToOemA|CharToOemW|"
"OemToChar|OemToCharA|OemToCharW|CharToOemBuffA|CharToOemBuffW|alloca|_"
"alloca|strlen|wcslen|_mbslen|_mbstrlen|StrLen|lstrlen|"
"ChangeWindowMessageFilter)\\z"));
"ChangeWindowMessageFilter|ChangeWindowMessageFilterEx)\\z"));
QList<ImportDescription> imports;
public:

View File

@ -7,7 +7,7 @@
#include <QAction>
/* Disassembly/Graph/Hexdump/Decompiler view priority */
enum class MemoryWidgetType { Disassembly, Graph, Hexdump, Decompiler };
enum class MemoryWidgetType { Disassembly, Graph, Hexdump, Decompiler, CallGraph, GlobalCallGraph };
class CUTTER_EXPORT MemoryDockWidget : public AddressableDockWidget
{

View File

@ -7,10 +7,16 @@ QuickFilterView::QuickFilterView(QWidget *parent, bool defaultOn)
{
ui->setupUi(this);
debounceTimer = new QTimer(this);
debounceTimer->setSingleShot(true);
connect(ui->closeFilterButton, &QAbstractButton::clicked, this, &QuickFilterView::closeFilter);
connect(debounceTimer, &QTimer::timeout, this,
[this]() { emit filterTextChanged(ui->filterLineEdit->text()); });
connect(ui->filterLineEdit, &QLineEdit::textChanged, this,
[this](const QString &text) { emit filterTextChanged(text); });
[this](const QString &text) { debounceTimer->start(150); });
if (!defaultOn) {
closeFilter();

View File

@ -7,6 +7,7 @@
#include <memory>
#include <QWidget>
#include <QTimer>
namespace Ui {
class QuickFilterView;
@ -31,6 +32,7 @@ signals:
private:
std::unique_ptr<Ui::QuickFilterView> ui;
QTimer *debounceTimer;
};
#endif // QUICKFILTERVIEW_H

View File

@ -254,6 +254,7 @@ void SearchWidget::refreshSearchspaces()
ui->searchspaceCombo->clear();
ui->searchspaceCombo->addItem(tr("asm code"), QVariant("/acj"));
ui->searchspaceCombo->addItem(tr("string"), QVariant("/j"));
ui->searchspaceCombo->addItem(tr("string (case insensitive)"), QVariant("/ij"));
ui->searchspaceCombo->addItem(tr("hex string"), QVariant("/xj"));
ui->searchspaceCombo->addItem(tr("ROP gadgets"), QVariant("/Rj"));
ui->searchspaceCombo->addItem(tr("32bit value"), QVariant("/vj"));
@ -301,13 +302,16 @@ void SearchWidget::updatePlaceholderText(int index)
case 1: // string
ui->filterLineEdit->setPlaceholderText("foobar");
break;
case 2: // hex string
case 2: // string (case insensitive)
ui->filterLineEdit->setPlaceholderText("FooBar");
break;
case 3: // hex string
ui->filterLineEdit->setPlaceholderText("deadbeef");
break;
case 3: // ROP gadgets
case 4: // ROP gadgets
ui->filterLineEdit->setPlaceholderText("pop,,pop");
break;
case 4: // 32bit value
case 5: // 32bit value
ui->filterLineEdit->setPlaceholderText("0xdeadbeef");
break;
default:

View File

@ -119,8 +119,9 @@ void SimpleTextGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block, b
p.setPen(palette().color(QPalette::WindowText));
// Render node text
auto x = block.x + padding;
int y = block.y + padding + p.fontMetrics().ascent();
QFontMetrics fm = QFontMetrics(p.font());
auto x = block.x + padding / 2;
int y = block.y + padding / 2 + fm.ascent();
p.drawText(QPoint(x, y), content.text);
}

View File

@ -259,6 +259,10 @@ void TypesWidget::showTypesContextMenu(const QPoint &pt)
void TypesWidget::on_actionExport_Types_triggered()
{
char *str = rz_core_types_as_c_all(Core()->core(), true);
if (!str) {
return;
}
QString filename =
QFileDialog::getSaveFileName(this, tr("Save File"), Config()->getRecentFolder());
if (filename.isEmpty()) {
@ -272,8 +276,8 @@ void TypesWidget::on_actionExport_Types_triggered()
return;
}
QTextStream fileOut(&file);
// TODO: use API for `tc` command once available
fileOut << Core()->cmd("tc");
fileOut << str;
free(str);
file.close();
}
@ -289,7 +293,6 @@ void TypesWidget::on_actionLoad_New_Types_triggered()
TypesInteractionDialog dialog(this);
connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);
dialog.setWindowTitle(tr("Load New Types"));
dialog.setTypeName(t.type);
dialog.exec();
}

View File

@ -21,8 +21,7 @@ VisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent)
graphicsView(new QGraphicsView),
seekGraphicsItem(nullptr),
PCGraphicsItem(nullptr),
main(main),
stats(nullptr, rz_core_analysis_stats_free)
main(main)
{
Q_UNUSED(parent);
@ -119,7 +118,7 @@ void VisualNavbar::fetchStats()
RzCoreLocked core(Core());
stats.reset(nullptr);
RzList *list = rz_core_get_boundaries_prot(core, -1, NULL, "search");
auto list = fromOwned(rz_core_get_boundaries_prot(core, -1, NULL, "search"));
if (!list) {
return;
}
@ -127,7 +126,7 @@ void VisualNavbar::fetchStats()
RzIOMap *map;
ut64 from = UT64_MAX;
ut64 to = 0;
CutterRzListForeach (list, iter, RzIOMap, map) {
CutterRzListForeach (list.get(), iter, RzIOMap, map) {
ut64 f = rz_itv_begin(map->itv);
ut64 t = rz_itv_end(map->itv);
if (f < from) {
@ -137,7 +136,6 @@ void VisualNavbar::fetchStats()
to = t;
}
}
rz_list_free(list);
to--; // rz_core_analysis_get_stats takes inclusive ranges
if (to < from) {
return;

View File

@ -47,7 +47,7 @@ private:
QGraphicsRectItem *PCGraphicsItem;
MainWindow *main;
std::unique_ptr<RzCoreAnalysisStats, decltype(&rz_core_analysis_stats_free)> stats;
UniquePtrC<RzCoreAnalysisStats, &rz_core_analysis_stats_free> stats;
unsigned int statsWidth = 0;
unsigned int previousWidth = 0;