mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-22 04:46:11 +00:00
Merge 'dev' branch into stable
This commit is contained in:
commit
341d42c3a2
@ -30,7 +30,6 @@ install:
|
|||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
- cmd: git submodule update --init --recursive
|
- cmd: git submodule update --init --recursive
|
||||||
- scripts\prepare_breakpad.bat
|
|
||||||
|
|
||||||
# Build config
|
# Build config
|
||||||
build_script:
|
build_script:
|
||||||
@ -48,7 +47,6 @@ build_script:
|
|||||||
-DCUTTER_PACKAGE_RZ_GHIDRA=ON
|
-DCUTTER_PACKAGE_RZ_GHIDRA=ON
|
||||||
-DCUTTER_PACKAGE_JSDEC=ON
|
-DCUTTER_PACKAGE_JSDEC=ON
|
||||||
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON
|
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON
|
||||||
-DCUTTER_ENABLE_CRASH_REPORTS=ON
|
|
||||||
-DCMAKE_PREFIX_PATH=%CUTTER_DEPS%\\pyside
|
-DCMAKE_PREFIX_PATH=%CUTTER_DEPS%\\pyside
|
||||||
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME%
|
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME%
|
||||||
-G Ninja
|
-G Ninja
|
||||||
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -2,7 +2,7 @@ blank_issues_enabled: false
|
|||||||
|
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Questions Telegram
|
- 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.
|
about: Please ask questions about Cutter here or one of the other community channels, not in the issue tracker.
|
||||||
|
|
||||||
- name: Questions IRC
|
- name: Questions IRC
|
||||||
|
53
.github/workflows/ci.yml
vendored
53
.github/workflows/ci.yml
vendored
@ -23,6 +23,7 @@ jobs:
|
|||||||
name: [
|
name: [
|
||||||
linux-x86_64,
|
linux-x86_64,
|
||||||
linux-x86_64-system-deps,
|
linux-x86_64-system-deps,
|
||||||
|
linux-x86_64-qt6-system-deps,
|
||||||
macos-x86_64,
|
macos-x86_64,
|
||||||
windows-x86_64,
|
windows-x86_64,
|
||||||
tarball
|
tarball
|
||||||
@ -39,6 +40,12 @@ jobs:
|
|||||||
system-deps: true
|
system-deps: true
|
||||||
cc-override: '/usr/bin/gcc-7'
|
cc-override: '/usr/bin/gcc-7'
|
||||||
cxx-override: '/usr/bin/g++-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
|
- name: linux-x86_64
|
||||||
os: ubuntu-18.04
|
os: ubuntu-18.04
|
||||||
python-version: 3.7.x
|
python-version: 3.7.x
|
||||||
@ -61,7 +68,7 @@ jobs:
|
|||||||
# Prevent one job from pausing the rest
|
# Prevent one job from pausing the rest
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
@ -69,32 +76,34 @@ jobs:
|
|||||||
if: contains(matrix.os, 'ubuntu')
|
if: contains(matrix.os, 'ubuntu')
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install libgraphviz-dev mesa-common-dev libxkbcommon-x11-dev libclang-8-dev llvm-8 ninja-build
|
sudo apt-get install libgraphviz-dev mesa-common-dev libxkbcommon-x11-dev ninja-build
|
||||||
if [[ "${{ matrix.os }}" = "ubuntu-18.04" ]]
|
if [[ "${{ matrix.os }}" = "ubuntu-18.04" || "${{ matrix.os }}" = "ubuntu-20.04" ]]
|
||||||
then
|
then
|
||||||
# install additional packages needed for appimage
|
# 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
|
fi
|
||||||
if [[ "${{ matrix.system-deps }}" = "true" ]]
|
if [[ "${{ matrix.os }}" = "ubuntu-18.04" && "${{ matrix.system-deps }}" = "true" ]]
|
||||||
then
|
then
|
||||||
sudo apt-get install qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools
|
sudo apt-get install qt5-default libqt5svg5-dev qttools5-dev qttools5-dev-tools
|
||||||
fi
|
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:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: homebrew dependencies
|
- name: homebrew dependencies
|
||||||
if: contains(matrix.os, 'macos')
|
if: contains(matrix.os, 'macos')
|
||||||
run: |
|
run: |
|
||||||
cd scripts
|
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
|
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
|
|
||||||
brew bundle
|
brew bundle
|
||||||
brew install coreutils
|
|
||||||
brew install pkg-config
|
|
||||||
- name: py dependencies
|
- name: py dependencies
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install -U pip==21.3.1
|
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
|
- name: Prepare package id
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@ -123,8 +132,6 @@ jobs:
|
|||||||
export CXX="${{matrix.cxx-override}}"
|
export CXX="${{matrix.cxx-override}}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
source scripts/prepare_breakpad_linux.sh
|
|
||||||
export PKG_CONFIG_PATH="$CUSTOM_BREAKPAD_PREFIX/lib/pkgconfig:${PKG_CONFIG_PATH:-}" #
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake --version
|
cmake --version
|
||||||
@ -138,8 +145,7 @@ jobs:
|
|||||||
-DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \
|
-DPYTHON_INCLUDE_DIR="$CUTTER_DEPS_PYTHON_PREFIX/include/python3.9" \
|
||||||
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
|
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
|
||||||
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
|
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
|
||||||
-DCUTTER_ENABLE_GRAPHVIZ=OFF \
|
-DCUTTER_ENABLE_GRAPHVIZ=ON \
|
||||||
-DCUTTER_ENABLE_CRASH_REPORTS=ON \
|
|
||||||
-DCUTTER_USE_BUNDLED_RIZIN=ON \
|
-DCUTTER_USE_BUNDLED_RIZIN=ON \
|
||||||
-DCUTTER_APPIMAGE_BUILD=ON \
|
-DCUTTER_APPIMAGE_BUILD=ON \
|
||||||
-DCUTTER_ENABLE_PACKAGING=ON \
|
-DCUTTER_ENABLE_PACKAGING=ON \
|
||||||
@ -152,6 +158,14 @@ jobs:
|
|||||||
-DCMAKE_INSTALL_PREFIX=appdir/usr \
|
-DCMAKE_INSTALL_PREFIX=appdir/usr \
|
||||||
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \
|
-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
|
else
|
||||||
cmake \
|
cmake \
|
||||||
-G Ninja \
|
-G Ninja \
|
||||||
@ -194,7 +208,6 @@ jobs:
|
|||||||
source cutter-deps/env.sh
|
source cutter-deps/env.sh
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export PATH=/usr/local/opt/llvm/bin:$PATH
|
export PATH=/usr/local/opt/llvm/bin:$PATH
|
||||||
source scripts/prepare_breakpad_macos.sh
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
PACKAGE_NAME=Cutter-${PACKAGE_ID}-macOS-x86_64
|
PACKAGE_NAME=Cutter-${PACKAGE_ID}-macOS-x86_64
|
||||||
@ -205,7 +218,6 @@ jobs:
|
|||||||
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
|
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3" \
|
||||||
-DCUTTER_ENABLE_PYTHON=ON \
|
-DCUTTER_ENABLE_PYTHON=ON \
|
||||||
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
|
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON \
|
||||||
-DCUTTER_ENABLE_CRASH_REPORTS=ON \
|
|
||||||
-DCUTTER_USE_BUNDLED_RIZIN=ON \
|
-DCUTTER_USE_BUNDLED_RIZIN=ON \
|
||||||
-DCUTTER_ENABLE_PACKAGING=ON \
|
-DCUTTER_ENABLE_PACKAGING=ON \
|
||||||
-DCUTTER_ENABLE_SIGDB=ON \
|
-DCUTTER_ENABLE_SIGDB=ON \
|
||||||
@ -216,7 +228,6 @@ jobs:
|
|||||||
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \
|
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \
|
||||||
-DCUTTER_PACKAGE_RZ_LIBYARA=ON \
|
-DCUTTER_PACKAGE_RZ_LIBYARA=ON \
|
||||||
-DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \
|
-DCPACK_PACKAGE_FILE_NAME="$PACKAGE_NAME" \
|
||||||
-DCMAKE_FRAMEWORK_PATH="$BREAKPAD_FRAMEWORK_DIR" \
|
|
||||||
-DCPACK_BUNDLE_APPLE_CERT_APP="-" \
|
-DCPACK_BUNDLE_APPLE_CERT_APP="-" \
|
||||||
.. && \
|
.. && \
|
||||||
make -j4;
|
make -j4;
|
||||||
@ -240,7 +251,6 @@ jobs:
|
|||||||
set CUTTER_DEPS=%CD%\cutter-deps
|
set CUTTER_DEPS=%CD%\cutter-deps
|
||||||
set PATH=%CD%\cutter-deps\qt\bin;%PATH%
|
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 "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||||
call scripts\prepare_breakpad.bat
|
|
||||||
cd
|
cd
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
@ -258,7 +268,6 @@ jobs:
|
|||||||
-DCUTTER_PACKAGE_RZ_LIBYARA=ON ^
|
-DCUTTER_PACKAGE_RZ_LIBYARA=ON ^
|
||||||
-DCUTTER_PACKAGE_JSDEC=ON ^
|
-DCUTTER_PACKAGE_JSDEC=ON ^
|
||||||
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^
|
-DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^
|
||||||
-DCUTTER_ENABLE_CRASH_REPORTS=ON ^
|
|
||||||
-DCMAKE_PREFIX_PATH="%CUTTER_DEPS%\pyside" ^
|
-DCMAKE_PREFIX_PATH="%CUTTER_DEPS%\pyside" ^
|
||||||
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME% ^
|
-DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME% ^
|
||||||
-G Ninja ^
|
-G Ninja ^
|
||||||
@ -276,7 +285,7 @@ jobs:
|
|||||||
echo PACKAGE_NAME=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV
|
echo PACKAGE_NAME=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV
|
||||||
echo PACKAGE_PATH=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
|
echo UPLOAD_ASSET_TYPE=application/gzip >> $GITHUB_ENV
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v3
|
||||||
if: env.PACKAGE_NAME != null
|
if: env.PACKAGE_NAME != null
|
||||||
with:
|
with:
|
||||||
name: ${{ env.PACKAGE_NAME }}
|
name: ${{ env.PACKAGE_NAME }}
|
||||||
@ -284,7 +293,7 @@ jobs:
|
|||||||
- name: Get release
|
- name: Get release
|
||||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
|
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
|
||||||
id: get_release
|
id: get_release
|
||||||
uses: karliss/get-release@23b8b7144dd5b0c9d6942b2fb78bd9ae71546d03
|
uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Upload release assets
|
- name: Upload release assets
|
||||||
|
8
.github/workflows/coverity-scan.yml
vendored
8
.github/workflows/coverity-scan.yml
vendored
@ -7,16 +7,16 @@ jobs:
|
|||||||
latest:
|
latest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- uses: actions/setup-python@v1
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.7.x
|
python-version: 3.9.x
|
||||||
|
|
||||||
- name: Download Coverity Build Tool
|
- name: Download Coverity Build Tool
|
||||||
run: |
|
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
|
mkdir cov-analysis-linux64
|
||||||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||||
env:
|
env:
|
||||||
|
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@ -7,9 +7,9 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: install dependencies
|
- name: install dependencies
|
||||||
|
4
.github/workflows/linter.yml
vendored
4
.github/workflows/linter.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
clang-format: ${{ steps.filter.outputs.clang-format }}
|
clang-format: ${{ steps.filter.outputs.clang-format }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: dorny/paths-filter@v2
|
- uses: dorny/paths-filter@v2
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
@ -33,7 +33,7 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.clang-format == 'true' }}
|
if: ${{ needs.changes.outputs.clang-format == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install wget
|
- name: Install wget
|
||||||
run: sudo apt --assume-yes install wget
|
run: sudo apt --assume-yes install wget
|
||||||
|
@ -12,7 +12,6 @@ pipeline:
|
|||||||
- export PACKAGE_ID=${CI_COMMIT_TAG=git-`date "+%Y-%m-%d"`-${CI_COMMIT_SHA}}
|
- export PACKAGE_ID=${CI_COMMIT_TAG=git-`date "+%Y-%m-%d"`-${CI_COMMIT_SHA}}
|
||||||
- export PACKAGE_NAME=Cutter-$${PACKAGE_ID}-macOS-arm64
|
- export PACKAGE_NAME=Cutter-$${PACKAGE_ID}-macOS-arm64
|
||||||
- source cutter-deps/env.sh
|
- source cutter-deps/env.sh
|
||||||
- source scripts/prepare_breakpad_macos.sh
|
|
||||||
- cmake -Bbuild -GNinja
|
- cmake -Bbuild -GNinja
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
-DPYTHON_LIBRARY="$$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.dylib"
|
-DPYTHON_LIBRARY="$$CUTTER_DEPS_PYTHON_PREFIX/lib/libpython3.9.dylib"
|
||||||
@ -20,7 +19,6 @@ pipeline:
|
|||||||
-DPYTHON_EXECUTABLE="$$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
|
-DPYTHON_EXECUTABLE="$$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
|
||||||
-DCUTTER_ENABLE_PYTHON=ON
|
-DCUTTER_ENABLE_PYTHON=ON
|
||||||
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
|
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
|
||||||
-DCUTTER_ENABLE_CRASH_REPORTS=ON
|
|
||||||
-DCUTTER_USE_BUNDLED_RIZIN=ON
|
-DCUTTER_USE_BUNDLED_RIZIN=ON
|
||||||
-DCUTTER_ENABLE_PACKAGING=ON
|
-DCUTTER_ENABLE_PACKAGING=ON
|
||||||
-DCUTTER_ENABLE_SIGDB=ON
|
-DCUTTER_ENABLE_SIGDB=ON
|
||||||
@ -31,7 +29,6 @@ pipeline:
|
|||||||
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON
|
-DCUTTER_PACKAGE_RZ_LIBSWIFT=ON
|
||||||
-DCUTTER_PACKAGE_RZ_LIBYARA=ON
|
-DCUTTER_PACKAGE_RZ_LIBYARA=ON
|
||||||
-DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME"
|
-DCPACK_PACKAGE_FILE_NAME="$$PACKAGE_NAME"
|
||||||
-DCMAKE_FRAMEWORK_PATH="$$BREAKPAD_FRAMEWORK_DIR"
|
|
||||||
-DCPACK_BUNDLE_APPLE_CERT_APP="-"
|
-DCPACK_BUNDLE_APPLE_CERT_APP="-"
|
||||||
- ninja -C build
|
- ninja -C build
|
||||||
package:
|
package:
|
||||||
|
@ -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)
|
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 "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_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)
|
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_KSYNTAXHIGHLIGHTING "Use KSyntaxHighlighting" AUTO)
|
||||||
tri_option(CUTTER_ENABLE_GRAPHVIZ "Enable use of graphviz for graph layout" 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_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_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_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_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_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_PACKAGE_JSDEC "Compile and install jsdec during install step." OFF)
|
||||||
OPTION(CUTTER_QT6 "Use QT6" OFF)
|
OPTION(CUTTER_QT6 "Use QT6" OFF)
|
||||||
|
|
||||||
@ -39,9 +38,33 @@ set(CUTTER_VERSION_MAJOR 2)
|
|||||||
set(CUTTER_VERSION_MINOR 1)
|
set(CUTTER_VERSION_MINOR 1)
|
||||||
set(CUTTER_VERSION_PATCH 2)
|
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)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
@ -130,7 +153,6 @@ if(CUTTER_USE_BUNDLED_RIZIN)
|
|||||||
endif()
|
endif()
|
||||||
message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}")
|
message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}")
|
||||||
message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}")
|
message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}")
|
||||||
message(STATUS "- Crash Handling: ${CUTTER_ENABLE_CRASH_REPORTS}")
|
|
||||||
message(STATUS "- KSyntaxHighlighting: ${KSYNTAXHIGHLIGHTING_STATUS}")
|
message(STATUS "- KSyntaxHighlighting: ${KSYNTAXHIGHLIGHTING_STATUS}")
|
||||||
message(STATUS "- Graphviz: ${CUTTER_ENABLE_GRAPHVIZ}")
|
message(STATUS "- Graphviz: ${CUTTER_ENABLE_GRAPHVIZ}")
|
||||||
message(STATUS "- Downloads dependencies: ${CUTTER_ENABLE_DEPENDENCY_DOWNLOADS}")
|
message(STATUS "- Downloads dependencies: ${CUTTER_ENABLE_DEPENDENCY_DOWNLOADS}")
|
||||||
|
@ -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)
|
[![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)
|
[![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)
|
![Screenshot](https://raw.githubusercontent.com/rizinorg/cutter/dev/docs/source/images/screenshot.png)
|
||||||
|
|
||||||
@ -23,10 +22,12 @@ 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):
|
- **macOS**: Download the `.dmg` file or use [Homebrew Cask](https://github.com/Homebrew/homebrew-cask):
|
||||||
|
|
||||||
`brew install --cask cutter`
|
`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`
|
`choco install cutter`
|
||||||
|
|
||||||
|
`scoop bucket add extras` followed by `scoop install cutter`
|
||||||
|
|
||||||
### Build from sources
|
### Build from sources
|
||||||
|
|
||||||
To build Cutter from sources, please check the [Building Docs](https://cutter.re/docs/building.html).
|
To build Cutter from sources, please check the [Building Docs](https://cutter.re/docs/building.html).
|
||||||
|
@ -40,6 +40,7 @@ ExternalProject_Add(Rizin-Bundled
|
|||||||
SOURCE_DIR "${RIZIN_SOURCE_DIR}"
|
SOURCE_DIR "${RIZIN_SOURCE_DIR}"
|
||||||
CONFIGURE_COMMAND "${MESON}" "<SOURCE_DIR>" ${MESON_OPTIONS} && "${MESON}" configure ${MESON_OPTIONS} --buildtype "$<$<CONFIG:Debug>:debug>$<$<NOT:$<CONFIG:Debug>>:release>"
|
CONFIGURE_COMMAND "${MESON}" "<SOURCE_DIR>" ${MESON_OPTIONS} && "${MESON}" configure ${MESON_OPTIONS} --buildtype "$<$<CONFIG:Debug>:debug>$<$<NOT:$<CONFIG:Debug>>:release>"
|
||||||
BUILD_COMMAND "${NINJA}"
|
BUILD_COMMAND "${NINJA}"
|
||||||
|
BUILD_ALWAYS TRUE
|
||||||
INSTALL_COMMAND "${NINJA}" install)
|
INSTALL_COMMAND "${NINJA}" install)
|
||||||
|
|
||||||
set(Rizin_INCLUDE_DIRS "${RIZIN_INSTALL_DIR}/include/librz" "${RIZIN_INSTALL_DIR}/include/librz/sdb")
|
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
|
# TODO: This version number should be fetched automatically
|
||||||
# instead of being hardcoded.
|
# 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
|
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_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_search rz_syscall rz_socket rz_magic rz_crypto rz_type rz_diff rz_sign
|
||||||
rz_demangler)
|
rz_demangler)
|
||||||
set (RZ_EXTRA_LIBS rz_main)
|
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
|
target_link_libraries(Rizin INTERFACE
|
||||||
${RZ_LIBS})
|
${RZ_LIBS})
|
||||||
|
@ -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
8
dist/CMakeLists.txt
vendored
@ -77,10 +77,6 @@ if(APPLE)
|
|||||||
set(CPACK_DMG_VOLUME_NAME "Cutter")
|
set(CPACK_DMG_VOLUME_NAME "Cutter")
|
||||||
set(CPACK_BUNDLE_APPLE_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/Entitlements.plist")
|
set(CPACK_BUNDLE_APPLE_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/macos/Entitlements.plist")
|
||||||
set(CPACK_APPLE_BUNDLE_ID "re.rizin.cutter")
|
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")
|
find_program(MACDEPLOYQT_PATH macdeployqt HINTS "${Qt5_DIR}/../../../bin")
|
||||||
if(NOT MACDEPLOYQT_PATH)
|
if(NOT MACDEPLOYQT_PATH)
|
||||||
@ -168,9 +164,9 @@ if(CUTTER_PACKAGE_RZ_GHIDRA)
|
|||||||
# installed Cutter.
|
# installed Cutter.
|
||||||
ExternalProject_Add(rz-ghidra
|
ExternalProject_Add(rz-ghidra
|
||||||
GIT_REPOSITORY https://github.com/rizinorg/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 c7a50a2e7c0a95cd52b167c9ee0fa1805223f08e
|
||||||
#GIT_TAG dev
|
GIT_TAG dev
|
||||||
#GIT_SHALLOW ON # disable this line when using commit hash
|
#GIT_SHALLOW ON # disable this line when using commit hash
|
||||||
CONFIGURE_COMMAND ""
|
CONFIGURE_COMMAND ""
|
||||||
BUILD_COMMAND ""
|
BUILD_COMMAND ""
|
||||||
|
8
dist/MacOSSetupBundle.cmake.in
vendored
8
dist/MacOSSetupBundle.cmake.in
vendored
@ -3,8 +3,6 @@ include(BundleUtilities)
|
|||||||
set(MACDEPLOYQT_PATH "@MACDEPLOYQT_PATH@")
|
set(MACDEPLOYQT_PATH "@MACDEPLOYQT_PATH@")
|
||||||
set(INFO_PLIST_PATH "@CPACK_BUNDLE_PLIST@")
|
set(INFO_PLIST_PATH "@CPACK_BUNDLE_PLIST@")
|
||||||
set(ADJUST_RIZIN_LIBS "@ADJUST_RIZIN_LIBS@")
|
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_PACKAGE_DEPENDENCIES "@CUTTER_PACKAGE_DEPENDENCIES@")
|
||||||
set(CUTTER_ENABLE_PYTHON "@CUTTER_ENABLE_PYTHON@")
|
set(CUTTER_ENABLE_PYTHON "@CUTTER_ENABLE_PYTHON@")
|
||||||
|
|
||||||
@ -82,9 +80,3 @@ foreach(_lib ${ADJUST_RIZIN_LIBS})
|
|||||||
get_filename_component(_name "${_lib}" NAME)
|
get_filename_component(_name "${_lib}" NAME)
|
||||||
file(REMOVE "${BUNDLE_PATH}/Contents/Frameworks/${_name}")
|
file(REMOVE "${BUNDLE_PATH}/Contents/Frameworks/${_name}")
|
||||||
endforeach()
|
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()
|
|
||||||
|
2
dist/bundle_python.ps1
vendored
2
dist/bundle_python.ps1
vendored
@ -2,7 +2,7 @@ $arch = $args[0]
|
|||||||
$dist = $args[1]
|
$dist = $args[1]
|
||||||
|
|
||||||
$py_version = (python --version).Split()[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_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"
|
$py_url = "https://www.python.org/ftp/python/${py_version}/python-${py_version}-embed-${py_platform}.zip"
|
||||||
|
|
||||||
|
@ -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
|
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:
|
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::
|
.. note::
|
||||||
|
|
||||||
If you are interested in building Cutter with support for Python plugins,
|
If you are interested in building Cutter with support for Python plugins,
|
||||||
Syntax Highlighting, Crash Reporting and more,
|
Syntax Highlighting and more, please look at the full list of `CMake Building Options`_.
|
||||||
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.
|
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 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_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.
|
* ``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
|
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
|
Troubleshooting
|
||||||
|
@ -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
2
rizin
@ -1 +1 @@
|
|||||||
Subproject commit 9023f8b997db210cef3b9a25cf1748fbc94942ed
|
Subproject commit 12898a365e70c22892d78abdd2627e7269533c5f
|
@ -4,3 +4,5 @@ brew "openssl"
|
|||||||
brew "xz"
|
brew "xz"
|
||||||
brew "llvm"
|
brew "llvm"
|
||||||
brew "meson"
|
brew "meson"
|
||||||
|
brew "coreutils"
|
||||||
|
brew "pkg-config"
|
||||||
|
@ -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',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -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
@ -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%
|
|
@ -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 ..
|
|
@ -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
|
|
@ -5,6 +5,7 @@ set(SOURCES
|
|||||||
Main.cpp
|
Main.cpp
|
||||||
core/Cutter.cpp
|
core/Cutter.cpp
|
||||||
core/CutterJson.cpp
|
core/CutterJson.cpp
|
||||||
|
core/RizinCpp.cpp
|
||||||
dialogs/EditStringDialog.cpp
|
dialogs/EditStringDialog.cpp
|
||||||
dialogs/WriteCommandsDialogs.cpp
|
dialogs/WriteCommandsDialogs.cpp
|
||||||
widgets/DisassemblerGraphView.cpp
|
widgets/DisassemblerGraphView.cpp
|
||||||
@ -72,7 +73,6 @@ set(SOURCES
|
|||||||
widgets/CutterTreeWidget.cpp
|
widgets/CutterTreeWidget.cpp
|
||||||
widgets/GraphWidget.cpp
|
widgets/GraphWidget.cpp
|
||||||
widgets/OverviewWidget.cpp
|
widgets/OverviewWidget.cpp
|
||||||
common/JsonTreeItem.cpp
|
|
||||||
common/JsonModel.cpp
|
common/JsonModel.cpp
|
||||||
dialogs/VersionInfoDialog.cpp
|
dialogs/VersionInfoDialog.cpp
|
||||||
widgets/FlirtWidget.cpp
|
widgets/FlirtWidget.cpp
|
||||||
@ -155,6 +155,7 @@ set(HEADER_FILES
|
|||||||
core/CutterCommon.h
|
core/CutterCommon.h
|
||||||
core/CutterDescriptions.h
|
core/CutterDescriptions.h
|
||||||
core/CutterJson.h
|
core/CutterJson.h
|
||||||
|
core/RizinCpp.h
|
||||||
dialogs/EditStringDialog.h
|
dialogs/EditStringDialog.h
|
||||||
dialogs/WriteCommandsDialogs.h
|
dialogs/WriteCommandsDialogs.h
|
||||||
widgets/DisassemblerGraphView.h
|
widgets/DisassemblerGraphView.h
|
||||||
@ -222,7 +223,6 @@ set(HEADER_FILES
|
|||||||
widgets/CutterTreeWidget.h
|
widgets/CutterTreeWidget.h
|
||||||
widgets/GraphWidget.h
|
widgets/GraphWidget.h
|
||||||
widgets/OverviewWidget.h
|
widgets/OverviewWidget.h
|
||||||
common/JsonTreeItem.h
|
|
||||||
common/JsonModel.h
|
common/JsonModel.h
|
||||||
dialogs/VersionInfoDialog.h
|
dialogs/VersionInfoDialog.h
|
||||||
widgets/FlirtWidget.h
|
widgets/FlirtWidget.h
|
||||||
@ -262,7 +262,6 @@ set(HEADER_FILES
|
|||||||
common/RunScriptTask.h
|
common/RunScriptTask.h
|
||||||
common/Json.h
|
common/Json.h
|
||||||
dialogs/EditMethodDialog.h
|
dialogs/EditMethodDialog.h
|
||||||
common/CrashHandler.h
|
|
||||||
dialogs/TypesInteractionDialog.h
|
dialogs/TypesInteractionDialog.h
|
||||||
widgets/SdbWidget.h
|
widgets/SdbWidget.h
|
||||||
plugins/PluginManager.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)
|
list(APPEND HEADER_FILES common/QtResImporter.h common/PythonManager.h common/PythonAPI.h)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CUTTER_ENABLE_CRASH_REPORTS)
|
|
||||||
list(APPEND SOURCES common/CrashHandler.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CUTTER_ENABLE_PYTHON_BINDINGS)
|
if(CUTTER_ENABLE_PYTHON_BINDINGS)
|
||||||
set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bindings")
|
set(BINDINGS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bindings")
|
||||||
set(BINDINGS_BUILD_DIR "${CMAKE_CURRENT_BINARY_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)
|
set_source_files_properties(${BINDINGS_SOURCE} PROPERTIES COMPILE_FLAGS -w)
|
||||||
endif()
|
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
|
set_target_properties(Cutter PROPERTIES
|
||||||
OUTPUT_NAME cutter
|
OUTPUT_NAME cutter
|
||||||
RUNTIME_OUTPUT_DIRECTORY ..
|
RUNTIME_OUTPUT_DIRECTORY ..
|
||||||
@ -452,6 +450,9 @@ set_target_properties(Cutter PROPERTIES
|
|||||||
CXX_VISIBILITY_PRESET hidden)
|
CXX_VISIBILITY_PRESET hidden)
|
||||||
target_compile_definitions(Cutter PRIVATE CUTTER_SOURCE_BUILD)
|
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 .)
|
set(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .)
|
||||||
foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES})
|
foreach(_dir ${CUTTER_INCLUDE_DIRECTORIES})
|
||||||
target_include_directories(Cutter PUBLIC
|
target_include_directories(Cutter PUBLIC
|
||||||
@ -465,19 +466,6 @@ if (TARGET Graphviz::GVC)
|
|||||||
target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_GRAPHVIZ)
|
target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_GRAPHVIZ)
|
||||||
endif()
|
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)
|
target_link_libraries(Cutter PUBLIC ${QT_PREFIX}::Core ${QT_PREFIX}::Widgets ${QT_PREFIX}::Gui PRIVATE ${QT_PREFIX}::Svg ${QT_PREFIX}::Network)
|
||||||
if (CUTTER_QT6)
|
if (CUTTER_QT6)
|
||||||
target_link_libraries(Cutter PUBLIC Qt6::Core5Compat Qt6::SvgWidgets)
|
target_link_libraries(Cutter PUBLIC Qt6::Core5Compat Qt6::SvgWidgets)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "common/PythonManager.h"
|
#include "common/PythonManager.h"
|
||||||
#include "common/CrashHandler.h"
|
|
||||||
#include "CutterApplication.h"
|
#include "CutterApplication.h"
|
||||||
#include "plugins/PluginManager.h"
|
#include "plugins/PluginManager.h"
|
||||||
#include "CutterConfig.h"
|
#include "CutterConfig.h"
|
||||||
|
15
src/Main.cpp
15
src/Main.cpp
@ -3,7 +3,6 @@
|
|||||||
#include "core/MainWindow.h"
|
#include "core/MainWindow.h"
|
||||||
#include "common/UpdateWorker.h"
|
#include "common/UpdateWorker.h"
|
||||||
#include "CutterConfig.h"
|
#include "CutterConfig.h"
|
||||||
#include "common/CrashHandler.h"
|
|
||||||
#include "common/SettingsUpgrade.h"
|
#include "common/SettingsUpgrade.h"
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@ -55,17 +54,6 @@ static void connectToConsole()
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
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
|
#ifdef Q_OS_WIN
|
||||||
connectToConsole();
|
connectToConsole();
|
||||||
#endif
|
#endif
|
||||||
@ -74,6 +62,9 @@ int main(int argc, char *argv[])
|
|||||||
qRegisterMetaType<QList<FunctionDescription>>();
|
qRegisterMetaType<QList<FunctionDescription>>();
|
||||||
|
|
||||||
QCoreApplication::setOrganizationName("rizin");
|
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");
|
QCoreApplication::setApplicationName("cutter");
|
||||||
|
|
||||||
// Importing settings after setting rename, needs separate handling in addition to regular version to version upgrade.
|
// Importing settings after setting rename, needs separate handling in addition to regular version to version upgrade.
|
||||||
|
@ -8,26 +8,18 @@
|
|||||||
|
|
||||||
void openIssue()
|
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;
|
QString url, osInfo, format, arch, type;
|
||||||
// Pull in info needed for git issue
|
// Pull in info needed for git issue
|
||||||
osInfo = QSysInfo::productType() + " "
|
osInfo = QSysInfo::productType() + " "
|
||||||
+ (QSysInfo::productVersion() == "unknown" ? "" : QSysInfo::productVersion());
|
+ (QSysInfo::productVersion() == "unknown" ? "" : QSysInfo::productVersion());
|
||||||
CutterJson docu = Core()->getFileInfo();
|
format = plugin && RZ_STR_ISNOTEMPTY(plugin->name) ? plugin->name : "N/A";
|
||||||
CutterJson coreObj = docu["core"];
|
arch = info && RZ_STR_ISNOTEMPTY(info->arch) ? info->arch : "N/A";
|
||||||
CutterJson binObj = docu["bin"];
|
type = info && RZ_STR_ISNOTEMPTY(info->type) ? info->type : "N/A";
|
||||||
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";
|
|
||||||
}
|
|
||||||
url = "https://github.com/rizinorg/cutter/issues/new?&body=**Environment information**\n* "
|
url = "https://github.com/rizinorg/cutter/issues/new?&body=**Environment information**\n* "
|
||||||
"Operating System: "
|
"Operating System: "
|
||||||
+ osInfo + "\n* Cutter version: " + CUTTER_VERSION_FULL + "\n* Obtained from:\n"
|
+ osInfo + "\n* Cutter version: " + CUTTER_VERSION_FULL + "\n* Obtained from:\n"
|
||||||
|
@ -201,7 +201,6 @@ void Configuration::resetAll()
|
|||||||
{
|
{
|
||||||
// Don't reset all rizin vars, that currently breaks a bunch of stuff.
|
// Don't reset all rizin vars, that currently breaks a bunch of stuff.
|
||||||
// settingsFile.remove()+loadInitials() should reset all settings configurable using Cutter GUI.
|
// settingsFile.remove()+loadInitials() should reset all settings configurable using Cutter GUI.
|
||||||
// Core()->cmdRaw("e-");
|
|
||||||
|
|
||||||
Core()->setSettings();
|
Core()->setSettings();
|
||||||
// Delete the file so no extra configuration is in it.
|
// Delete the file so no extra configuration is in it.
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
@ -49,8 +49,7 @@ void DecompilerHighlighter::highlightBlock(const QString &)
|
|||||||
size_t start = block.position();
|
size_t start = block.position();
|
||||||
size_t end = block.position() + block.length();
|
size_t end = block.position() + block.length();
|
||||||
|
|
||||||
std::unique_ptr<RzPVector, decltype(&rz_pvector_free)> annotations(
|
auto annotations = fromOwned(rz_annotated_code_annotations_range(code, start, end));
|
||||||
rz_annotated_code_annotations_range(code, start, end), &rz_pvector_free);
|
|
||||||
void **iter;
|
void **iter;
|
||||||
rz_pvector_foreach(annotations.get(), iter)
|
rz_pvector_foreach(annotations.get(), iter)
|
||||||
{
|
{
|
||||||
|
@ -9,15 +9,6 @@ Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)
|
|||||||
|
|
||||||
core = Core();
|
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));
|
regFormat.setForeground(QColor(236, 100, 75));
|
||||||
|
|
||||||
for (const QString &pattern : this->core->regs) {
|
for (const QString &pattern : this->core->regs) {
|
||||||
|
@ -1,123 +1,38 @@
|
|||||||
#include "JsonModel.h"
|
#include "JsonModel.h"
|
||||||
|
|
||||||
#include <QIODevice>
|
QTreeWidgetItem *Cutter::jsonTreeWidgetItem(const QString &key, const CutterJson &json)
|
||||||
|
|
||||||
JsonModel::JsonModel(QObject *parent) : QAbstractItemModel(parent)
|
|
||||||
{
|
{
|
||||||
mRootItem = new JsonTreeItem;
|
QString val;
|
||||||
mHeaders.append("key");
|
switch (json.type()) {
|
||||||
mHeaders.append("value");
|
case RZ_JSON_STRING:
|
||||||
}
|
val = json.toString();
|
||||||
|
break;
|
||||||
JsonModel::~JsonModel()
|
case RZ_JSON_BOOLEAN:
|
||||||
{
|
val = json.toBool() ? "true" : "false";
|
||||||
delete mRootItem;
|
break;
|
||||||
}
|
case RZ_JSON_DOUBLE:
|
||||||
|
val = QString::number(json.lowLevelValue()->num.dbl_value);
|
||||||
bool JsonModel::load(QIODevice *device)
|
break;
|
||||||
{
|
case RZ_JSON_INTEGER:
|
||||||
return loadJson(device->readAll());
|
val = QString::number(json.toUt64());
|
||||||
}
|
break;
|
||||||
|
case RZ_JSON_NULL:
|
||||||
bool JsonModel::loadJson(const QByteArray &json)
|
val = "null";
|
||||||
{
|
break;
|
||||||
mDocument = QJsonDocument::fromJson(json);
|
case RZ_JSON_OBJECT:
|
||||||
|
case RZ_JSON_ARRAY:
|
||||||
if (!mDocument.isNull()) {
|
break;
|
||||||
beginResetModel();
|
}
|
||||||
delete mRootItem;
|
auto r = new QTreeWidgetItem(QStringList({ key, val }));
|
||||||
if (mDocument.isArray()) {
|
if (json.type() == RZ_JSON_ARRAY) {
|
||||||
mRootItem = JsonTreeItem::load(QJsonValue(mDocument.array()));
|
size_t i = 0;
|
||||||
} else {
|
for (const auto &child : json) {
|
||||||
mRootItem = JsonTreeItem::load(QJsonValue(mDocument.object()));
|
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;
|
return r;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -2,37 +2,13 @@
|
|||||||
#ifndef JSONMODEL_H
|
#ifndef JSONMODEL_H
|
||||||
#define JSONMODEL_H
|
#define JSONMODEL_H
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QTreeWidgetItem>
|
||||||
#include <QJsonDocument>
|
#include "CutterJson.h"
|
||||||
#include <QJsonValue>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QIcon>
|
|
||||||
|
|
||||||
#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
|
#endif // JSONMODEL_H
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
@ -16,7 +16,10 @@ void RunScriptTask::runTask()
|
|||||||
{
|
{
|
||||||
if (!this->fileName.isNull()) {
|
if (!this->fileName.isNull()) {
|
||||||
log(tr("Executing script..."));
|
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()) {
|
if (isInterrupted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
* {
|
* {
|
||||||
* TempConfig tempConfig;
|
* TempConfig tempConfig;
|
||||||
* tempConfig.set("asm.arch", "x86").set("asm.comments", false);
|
* tempConfig.set("asm.arch", "x86").set("asm.comments", false);
|
||||||
* return Core()->cmdRaw("pd");
|
|
||||||
* // config automatically restored at the end of scope
|
* // config automatically restored at the end of scope
|
||||||
* }
|
* }
|
||||||
* \endcode
|
* \endcode
|
||||||
|
@ -120,13 +120,6 @@ static void updateOwnedCharPtr(char *&variable, const QString &newValue)
|
|||||||
variable = strdup(data.data());
|
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)
|
static bool reg_sync(RzCore *core, RzRegisterType type, bool write)
|
||||||
{
|
{
|
||||||
if (rz_core_is_debug(core)) {
|
if (rz_core_is_debug(core)) {
|
||||||
@ -377,6 +370,37 @@ QString CutterCore::cmd(const char *str)
|
|||||||
return o;
|
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()
|
bool CutterCore::isRedirectableDebugee()
|
||||||
{
|
{
|
||||||
if (!currentlyDebugging || currentlyAttachedToPID != -1) {
|
if (!currentlyDebugging || currentlyAttachedToPID != -1) {
|
||||||
@ -406,48 +430,6 @@ bool CutterCore::isDebugTaskInProgress()
|
|||||||
return false;
|
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)
|
bool CutterCore::asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<RizinTask> &task)
|
||||||
{
|
{
|
||||||
if (!task.isNull()) {
|
if (!task.isNull()) {
|
||||||
@ -468,6 +450,13 @@ bool CutterCore::asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<R
|
|||||||
return true;
|
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 CutterCore::cmdRawAt(const char *cmd, RVA address)
|
||||||
{
|
{
|
||||||
QString res;
|
QString res;
|
||||||
@ -486,8 +475,8 @@ QString CutterCore::cmdRaw(const char *cmd)
|
|||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
rz_cons_push();
|
rz_cons_push();
|
||||||
|
|
||||||
// rz_cmd_call does not return the output of the command
|
// rz_core_cmd does not return the output of the command
|
||||||
rz_cmd_call(core->rcmd, cmd);
|
rz_core_cmd(core, cmd, 0);
|
||||||
|
|
||||||
// we grab the output straight from rz_cons
|
// we grab the output straight from rz_cons
|
||||||
res = rz_cons_get_buffer();
|
res = rz_cons_get_buffer();
|
||||||
@ -510,18 +499,6 @@ CutterJson CutterCore::cmdj(const char *str)
|
|||||||
return parseJson(res, 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)
|
QString CutterCore::cmdTask(const QString &str)
|
||||||
{
|
{
|
||||||
RizinCmdTask task(str);
|
RizinCmdTask task(str);
|
||||||
@ -530,14 +507,6 @@ QString CutterCore::cmdTask(const QString &str)
|
|||||||
return task.getResult();
|
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)
|
CutterJson CutterCore::parseJson(char *res, const char *cmd)
|
||||||
{
|
{
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@ -687,7 +656,7 @@ bool CutterCore::mapFile(QString path, RVA mapaddr)
|
|||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
RVA addr = mapaddr != RVA_INVALID ? mapaddr : 0;
|
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)) {
|
if (rz_core_file_open(core, path.toUtf8().constData(), RZ_PERM_RX, addr)) {
|
||||||
rz_core_bin_load(core, path.toUtf8().constData(), baddr);
|
rz_core_bin_load(core, path.toUtf8().constData(), baddr);
|
||||||
} else {
|
} else {
|
||||||
@ -746,34 +715,31 @@ void CutterCore::delFlag(const QString &name)
|
|||||||
emit flagsChanged();
|
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)
|
QString CutterCore::getInstructionBytes(RVA addr)
|
||||||
{
|
{
|
||||||
auto ret = (char *)Core()->returnAtSeek(
|
auto ab = getRzAnalysisBytesSingle(addr);
|
||||||
[&]() {
|
return ab ? ab->bytes : "";
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CutterCore::getInstructionOpcode(RVA addr)
|
QString CutterCore::getInstructionOpcode(RVA addr)
|
||||||
{
|
{
|
||||||
auto ret = (char *)Core()->returnAtSeek(
|
auto ab = getRzAnalysisBytesSingle(addr);
|
||||||
[&]() {
|
return ab ? ab->opcode : "";
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CutterCore::editInstruction(RVA addr, const QString &inst, bool fillWithNops)
|
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)
|
void CutterCore::nopInstruction(RVA addr)
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
applyAtSeek([&]() { rz_core_hack(core, "nop"); }, addr);
|
{
|
||||||
|
auto seek = seekTemp(addr);
|
||||||
|
rz_core_hack(core, "nop");
|
||||||
|
}
|
||||||
emit instructionChanged(addr);
|
emit instructionChanged(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CutterCore::jmpReverse(RVA addr)
|
void CutterCore::jmpReverse(RVA addr)
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
applyAtSeek([&]() { rz_core_hack(core, "recj"); }, addr);
|
{
|
||||||
|
auto seek = seekTemp(addr);
|
||||||
|
rz_core_hack(core, "recj");
|
||||||
|
}
|
||||||
emit instructionChanged(addr);
|
emit instructionChanged(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,8 +851,8 @@ QString CutterCore::getString(RVA addr, uint64_t len, RzStrEnc encoding, bool es
|
|||||||
opt.length = len;
|
opt.length = len;
|
||||||
opt.encoding = encoding;
|
opt.encoding = encoding;
|
||||||
opt.escape_nl = escape_nl;
|
opt.escape_nl = escape_nl;
|
||||||
char *s = (char *)returnAtSeek([&]() { return rz_str_stringify_raw_buffer(&opt, NULL); }, addr);
|
auto seek = seekTemp(addr);
|
||||||
return fromOwnedCharPtr(s);
|
return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CutterCore::getMetaString(RVA addr)
|
QString CutterCore::getMetaString(RVA addr)
|
||||||
@ -964,12 +936,11 @@ void CutterCore::applyStructureOffset(const QString &structureOffset, RVA offset
|
|||||||
offset = getOffset();
|
offset = getOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAtSeek(
|
{
|
||||||
[&]() {
|
CORE_LOCK();
|
||||||
CORE_LOCK();
|
auto seek = seekTemp(offset);
|
||||||
rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData());
|
rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData());
|
||||||
},
|
}
|
||||||
offset);
|
|
||||||
emit instructionChanged(offset);
|
emit instructionChanged(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1055,24 +1026,20 @@ RVA CutterCore::prevOpAddr(RVA startAddr, int count)
|
|||||||
RVA CutterCore::nextOpAddr(RVA startAddr, int count)
|
RVA CutterCore::nextOpAddr(RVA startAddr, int count)
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
|
auto seek = seekTemp(startAddr);
|
||||||
|
auto vec =
|
||||||
|
fromOwned(rz_core_analysis_bytes(core, core->block, (int)core->blocksize, count + 1));
|
||||||
|
|
||||||
CutterJson array =
|
RVA addr = startAddr + 1;
|
||||||
Core()->cmdj("pdj " + QString::number(count + 1) + " @ " + QString::number(startAddr));
|
if (!vec) {
|
||||||
if (!array.size()) {
|
return addr;
|
||||||
return startAddr + 1;
|
|
||||||
}
|
}
|
||||||
|
auto ab = reinterpret_cast<RzAnalysisBytes *>(rz_pvector_tail(vec.get()));
|
||||||
CutterJson instValue = array.last();
|
if (!(ab && ab->op)) {
|
||||||
if (instValue.type() != RZ_JSON_OBJECT) {
|
return addr;
|
||||||
return startAddr + 1;
|
|
||||||
}
|
}
|
||||||
|
addr = ab->op->addr;
|
||||||
RVA offset = instValue[RJsonKey::offset].toRVA();
|
return addr;
|
||||||
if (offset == RVA_INVALID) {
|
|
||||||
return startAddr + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RVA CutterCore::getOffset()
|
RVA CutterCore::getOffset()
|
||||||
@ -1296,7 +1263,8 @@ QString CutterCore::disassemble(const QByteArray &data)
|
|||||||
|
|
||||||
QString CutterCore::disassembleSingleInstruction(RVA addr)
|
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)
|
RzAnalysisFunction *CutterCore::functionIn(ut64 addr)
|
||||||
@ -1370,16 +1338,6 @@ QString CutterCore::flagAt(RVA addr)
|
|||||||
return core->flags->realnames && f->realname ? f->realname : f->name;
|
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)
|
void CutterCore::createFunctionAt(RVA addr)
|
||||||
{
|
{
|
||||||
createFunctionAt(addr, "");
|
createFunctionAt(addr, "");
|
||||||
@ -1400,19 +1358,8 @@ void CutterCore::createFunctionAt(RVA addr, QString name)
|
|||||||
|
|
||||||
RVA CutterCore::getOffsetJump(RVA addr)
|
RVA CutterCore::getOffsetJump(RVA addr)
|
||||||
{
|
{
|
||||||
auto rva = (RVA *)Core()->returnAtSeek(
|
auto ab = getRzAnalysisBytesSingle(addr);
|
||||||
[&]() {
|
return ab && ab->op ? ab->op->jump : RVA_INVALID;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Decompiler *> CutterCore::getDecompilers()
|
QList<Decompiler *> CutterCore::getDecompilers()
|
||||||
@ -1440,17 +1387,7 @@ bool CutterCore::registerDecompiler(Decompiler *decompiler)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CutterJson CutterCore::getFileInfo()
|
CutterJson CutterCore::getSignatureInfo()
|
||||||
{
|
|
||||||
return cmdj("ij");
|
|
||||||
}
|
|
||||||
|
|
||||||
CutterJson CutterCore::getFileVersionInfo()
|
|
||||||
{
|
|
||||||
return cmdj("iVj");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CutterCore::getSignatureInfo()
|
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
RzBinFile *cur = rz_bin_cur(core->bin);
|
RzBinFile *cur = rz_bin_cur(core->bin);
|
||||||
@ -1462,7 +1399,17 @@ QString CutterCore::getSignatureInfo()
|
|||||||
if (!signature) {
|
if (!signature) {
|
||||||
return {};
|
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
|
// 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)) {
|
for (auto var : CutterPVector<RzAnalysisVar>(&fcn->vars)) {
|
||||||
VariableDescription desc;
|
VariableDescription desc;
|
||||||
switch (var->kind) {
|
desc.storageType = var->storage.type;
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (!var->name || !var->type) {
|
if (!var->name || !var->type) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2139,9 +2075,15 @@ void CutterCore::attachDebug(int pid)
|
|||||||
offsetPriorDebugging = getOffset();
|
offsetPriorDebugging = getOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString attach_command = currentlyOpenFile.isEmpty() ? "o" : "oodf";
|
CORE_LOCK();
|
||||||
// attach to process with dbg plugin
|
setConfig("cfg.debug", true);
|
||||||
asyncCmd("e cfg.debug=true;" + attach_command + " dbg://" + QString::number(pid), debugTask);
|
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();
|
emit debugTaskStateChanged();
|
||||||
|
|
||||||
@ -2195,7 +2137,8 @@ void CutterCore::stopDebug()
|
|||||||
|
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
if (currentlyEmulating) {
|
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();
|
resetWriteCache();
|
||||||
rz_core_debug_clear_register_flags(core);
|
rz_core_debug_clear_register_flags(core);
|
||||||
rz_core_analysis_esil_trace_stop(core);
|
rz_core_analysis_esil_trace_stop(core);
|
||||||
@ -2919,11 +2862,9 @@ bool CutterCore::isGraphEmpty()
|
|||||||
return emptyGraph;
|
return emptyGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CutterCore::getOpcodes()
|
void CutterCore::getRegs()
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
this->opcodes = cmdList("?O");
|
|
||||||
|
|
||||||
this->regs = {};
|
this->regs = {};
|
||||||
const RzList *rs = rz_reg_get_list(getReg(), RZ_REG_TYPE_ANY);
|
const RzList *rs = rz_reg_get_list(getReg(), RZ_REG_TYPE_ANY);
|
||||||
if (!rs) {
|
if (!rs) {
|
||||||
@ -3099,12 +3040,8 @@ QList<FunctionDescription> CutterCore::getAllFunctions()
|
|||||||
FunctionDescription function;
|
FunctionDescription function;
|
||||||
function.offset = fcn->addr;
|
function.offset = fcn->addr;
|
||||||
function.linearSize = rz_analysis_function_linear_size(fcn);
|
function.linearSize = rz_analysis_function_linear_size(fcn);
|
||||||
function.nargs = rz_analysis_var_count(core->analysis, fcn, 'b', 1)
|
function.nargs = rz_analysis_arg_count(fcn);
|
||||||
+ rz_analysis_var_count(core->analysis, fcn, 'r', 1)
|
function.nlocals = rz_analysis_var_local_count(fcn);
|
||||||
+ 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.nbbs = rz_list_length(fcn->bbs);
|
function.nbbs = rz_list_length(fcn->bbs);
|
||||||
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
|
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
|
||||||
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
|
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
|
||||||
@ -3179,21 +3116,38 @@ QList<ImportDescription> CutterCore::getAllImports()
|
|||||||
QList<ExportDescription> CutterCore::getAllExports()
|
QList<ExportDescription> CutterCore::getAllExports()
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
QList<ExportDescription> ret;
|
RzBinFile *bf = rz_bin_cur(core->bin);
|
||||||
|
if (!bf) {
|
||||||
for (CutterJson exportObject : cmdj("iEj")) {
|
return {};
|
||||||
ExportDescription exp;
|
}
|
||||||
|
const RzList *symbols = rz_bin_object_get_symbols(bf->o);
|
||||||
exp.vaddr = exportObject[RJsonKey::vaddr].toRVA();
|
if (!symbols) {
|
||||||
exp.paddr = exportObject[RJsonKey::paddr].toRVA();
|
return {};
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3349,22 +3303,37 @@ QList<RelocDescription> CutterCore::getAllRelocs()
|
|||||||
|
|
||||||
QList<StringDescription> CutterCore::getAllStrings()
|
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;
|
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;
|
StringDescription string;
|
||||||
|
string.string = rz_str_escape_utf8_keep_printable(str->string, &opt);
|
||||||
string.string = value[RJsonKey::string].toString();
|
string.vaddr = obj ? rva(obj, str->paddr, str->vaddr, va) : str->paddr;
|
||||||
string.vaddr = value[RJsonKey::vaddr].toRVA();
|
string.type = rz_str_enc_as_string(str->type);
|
||||||
string.type = value[RJsonKey::type].toString();
|
string.size = str->size;
|
||||||
string.size = value[RJsonKey::size].toUt64();
|
string.length = str->length;
|
||||||
string.length = value[RJsonKey::length].toUt64();
|
string.section = section ? section->name : "";
|
||||||
string.section = value[RJsonKey::section].toString();
|
|
||||||
|
|
||||||
ret << string;
|
ret << string;
|
||||||
}
|
}
|
||||||
@ -3881,7 +3850,7 @@ QList<TypeDescription> CutterCore::getBaseType(RzBaseTypeKind kind, const char *
|
|||||||
|
|
||||||
exp.type = type->name;
|
exp.type = type->name;
|
||||||
exp.size = rz_type_db_base_get_bitsize(core->analysis->typedb, type);
|
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);
|
exp.category = tr(category);
|
||||||
types << exp;
|
types << exp;
|
||||||
}
|
}
|
||||||
@ -3923,8 +3892,7 @@ QString CutterCore::getTypeAsC(QString name)
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
char *earg = rz_cmd_escape_arg(name.toUtf8().constData(), RZ_CMD_ESCAPE_ONE_ARG);
|
char *earg = rz_cmd_escape_arg(name.toUtf8().constData(), RZ_CMD_ESCAPE_ONE_ARG);
|
||||||
// TODO: use API for `tc` command once available
|
QString result = fromOwnedCharPtr(rz_core_types_as_c(core, earg, true));
|
||||||
QString result = cmd(QString("tc %1").arg(earg));
|
|
||||||
free(earg);
|
free(earg);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -3979,22 +3947,34 @@ QList<SearchDescription> CutterCore::getAllSearch(QString searchFor, QString spa
|
|||||||
QList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites,
|
QList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites,
|
||||||
RVA offset)
|
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>();
|
QList<XrefDescription> xrefList = QList<XrefDescription>();
|
||||||
for (CutterJson xrefObject : cmdjAt(findWrites ? "afvWj" : "afvRj", offset)) {
|
for (const auto &v : CutterPVector<RzAnalysisVar>(&fcn->vars)) {
|
||||||
QString name = xrefObject[RJsonKey::name].toString();
|
if (variableName != v->name) {
|
||||||
if (name == variableName) {
|
continue;
|
||||||
for (CutterJson address : xrefObject[RJsonKey::addrs]) {
|
}
|
||||||
XrefDescription xref;
|
RzAnalysisVarAccess *acc;
|
||||||
RVA addr = address.toRVA();
|
CutterRzVectorForeach(&v->accesses, acc, RzAnalysisVarAccess)
|
||||||
xref.from = addr;
|
{
|
||||||
xref.to = addr;
|
if (!(acc->type & typ)) {
|
||||||
if (findWrites) {
|
continue;
|
||||||
xref.from_str = RzAddressString(addr);
|
|
||||||
} else {
|
|
||||||
xref.to_str = RzAddressString(addr);
|
|
||||||
}
|
|
||||||
xrefList << xref;
|
|
||||||
}
|
}
|
||||||
|
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;
|
return xrefList;
|
||||||
@ -4138,18 +4118,33 @@ void CutterCore::loadPDB(const QString &file)
|
|||||||
|
|
||||||
QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
|
QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
|
||||||
{
|
{
|
||||||
CutterJson array = cmdj(QString("pdJ ") + QString::number(lines) + QString(" @ ")
|
CORE_LOCK();
|
||||||
+ QString::number(offset));
|
auto vec = fromOwned(
|
||||||
QList<DisassemblyLine> r;
|
rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));
|
||||||
|
if (!vec) {
|
||||||
for (CutterJson object : array) {
|
return {};
|
||||||
DisassemblyLine line;
|
|
||||||
line.offset = object[RJsonKey::offset].toRVA();
|
|
||||||
line.text = ansiEscapeToHtml(object[RJsonKey::text].toString());
|
|
||||||
line.arrow = object[RJsonKey::arrow].toRVA();
|
|
||||||
r << line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4161,28 +4156,36 @@ QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
|
|||||||
*/
|
*/
|
||||||
QString CutterCore::hexdump(RVA address, int size, HexdumpFormats format)
|
QString CutterCore::hexdump(RVA address, int size, HexdumpFormats format)
|
||||||
{
|
{
|
||||||
QString command = "px";
|
CORE_LOCK();
|
||||||
|
char *res = nullptr;
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case HexdumpFormats::Normal:
|
case HexdumpFormats::Normal:
|
||||||
|
res = rz_core_print_hexdump_or_hexdiff_str(core, RZ_OUTPUT_MODE_STANDARD, address, size,
|
||||||
|
false);
|
||||||
break;
|
break;
|
||||||
case HexdumpFormats::Half:
|
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;
|
break;
|
||||||
case HexdumpFormats::Word:
|
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;
|
break;
|
||||||
case HexdumpFormats::Quad:
|
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;
|
break;
|
||||||
case HexdumpFormats::Signed:
|
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;
|
break;
|
||||||
case HexdumpFormats::Octal:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmdRawAt(QString("%1 %2").arg(command).arg(size), address);
|
return fromOwnedCharPtr(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray CutterCore::hexStringToBytes(const QString &hex)
|
QByteArray CutterCore::hexStringToBytes(const QString &hex)
|
||||||
@ -4212,10 +4215,9 @@ void CutterCore::loadScript(const QString &scriptname)
|
|||||||
triggerRefreshAll();
|
triggerRefreshAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CutterCore::getRizinVersionReadable()
|
QString CutterCore::getRizinVersionReadable(const char *program)
|
||||||
{
|
{
|
||||||
return QString("%1 (%2)").arg(QString::fromUtf8(RZ_VERSION),
|
return fromOwnedCharPtr(rz_version_str(program));
|
||||||
QString::fromUtf8(RZ_GITTIP).left(7));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CutterCore::getVersionInformation()
|
QString CutterCore::getVersionInformation()
|
||||||
@ -4238,7 +4240,7 @@ QString CutterCore::getVersionInformation()
|
|||||||
{ "rz_crypto", &rz_crypto_version },
|
{ "rz_crypto", &rz_crypto_version },
|
||||||
{ "rz_bp", &rz_bp_version },
|
{ "rz_bp", &rz_bp_version },
|
||||||
{ "rz_debug", &rz_debug_version },
|
{ "rz_debug", &rz_debug_version },
|
||||||
{ "rz_msg_digest", &rz_msg_digest_version },
|
{ "rz_hash", &rz_hash_version },
|
||||||
{ "rz_io", &rz_io_version },
|
{ "rz_io", &rz_io_version },
|
||||||
#if !USE_LIB_MAGIC
|
#if !USE_LIB_MAGIC
|
||||||
{ "rz_magic", &rz_magic_version },
|
{ "rz_magic", &rz_magic_version },
|
||||||
@ -4252,7 +4254,8 @@ QString CutterCore::getVersionInformation()
|
|||||||
/* ... */
|
/* ... */
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
versionInfo.append(QString("%1 rz\n").arg(getRizinVersionReadable()));
|
versionInfo.append(getRizinVersionReadable());
|
||||||
|
versionInfo.append("\n");
|
||||||
for (i = 0; vcs[i].name; i++) {
|
for (i = 0; vcs[i].name; i++) {
|
||||||
struct vcs_t *v = &vcs[i];
|
struct vcs_t *v = &vcs[i];
|
||||||
const char *name = v->callback();
|
const char *name = v->callback();
|
||||||
@ -4316,7 +4319,7 @@ QString CutterCore::ansiEscapeToHtml(const QString &text)
|
|||||||
int len;
|
int len;
|
||||||
char *html = rz_cons_html_filter(text.toUtf8().constData(), &len);
|
char *html = rz_cons_html_filter(text.toUtf8().constData(), &len);
|
||||||
if (!html) {
|
if (!html) {
|
||||||
return QString();
|
return {};
|
||||||
}
|
}
|
||||||
QString r = QString::fromUtf8(html, len);
|
QString r = QString::fromUtf8(html, len);
|
||||||
rz_mem_free(html);
|
rz_mem_free(html);
|
||||||
@ -4491,7 +4494,6 @@ QByteArray CutterCore::ioRead(RVA addr, int len)
|
|||||||
/* Zero-copy */
|
/* Zero-copy */
|
||||||
array.resize(len);
|
array.resize(len);
|
||||||
if (!rz_io_read_at(core->io, addr, (uint8_t *)array.data(), len)) {
|
if (!rz_io_read_at(core->io, addr, (uint8_t *)array.data(), len)) {
|
||||||
qWarning() << "Can't read data" << addr << len;
|
|
||||||
array.fill(0xff);
|
array.fill(0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4501,19 +4503,80 @@ QByteArray CutterCore::ioRead(RVA addr, int len)
|
|||||||
QStringList CutterCore::getConfigVariableSpaces(const QString &key)
|
QStringList CutterCore::getConfigVariableSpaces(const QString &key)
|
||||||
{
|
{
|
||||||
CORE_LOCK();
|
CORE_LOCK();
|
||||||
QStringList stringList;
|
RzList *list = rz_core_config_in_space(core, key.toUtf8().constData());
|
||||||
for (const auto &node : CutterRzList<RzConfigNode>(core->config->nodes)) {
|
if (!list) {
|
||||||
stringList.push_back(node->name);
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key.isEmpty()) {
|
QStringList stringList;
|
||||||
stringList = stringList.filter(QRegularExpression(QString("^%0\\..*").arg(key)));
|
for (const auto &x : CutterRzList<char>(list)) {
|
||||||
std::transform(stringList.begin(), stringList.end(), stringList.begin(),
|
stringList << x;
|
||||||
[](const QString &x) { return x.split('.').last(); });
|
|
||||||
} else {
|
|
||||||
std::transform(stringList.begin(), stringList.end(), stringList.begin(),
|
|
||||||
[](const QString &x) { return x.split('.').first(); });
|
|
||||||
}
|
}
|
||||||
stringList.removeDuplicates();
|
rz_list_free(list);
|
||||||
return stringList;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class AsyncTaskManager;
|
class AsyncTaskManager;
|
||||||
class BasicInstructionHighlighter;
|
class BasicInstructionHighlighter;
|
||||||
@ -31,6 +32,7 @@ class RizinTaskDialog;
|
|||||||
#include "common/Helpers.h"
|
#include "common/Helpers.h"
|
||||||
|
|
||||||
#include <rz_project.h>
|
#include <rz_project.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#define Core() (CutterCore::instance())
|
#define Core() (CutterCore::instance())
|
||||||
|
|
||||||
@ -59,6 +61,8 @@ struct CUTTER_EXPORT RegisterRef
|
|||||||
QString name;
|
QString name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using PRzAnalysisBytes = std::unique_ptr<RzAnalysisBytes, decltype(rz_analysis_bytes_free) *>;
|
||||||
|
|
||||||
class CUTTER_EXPORT CutterCore : public QObject
|
class CUTTER_EXPORT CutterCore : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -81,6 +85,10 @@ public:
|
|||||||
RVA getOffset() const { return core_->offset; }
|
RVA getOffset() const { return core_->offset; }
|
||||||
|
|
||||||
/* Core functions (commands) */
|
/* 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);
|
static QString sanitizeStringForCommand(QString s);
|
||||||
/**
|
/**
|
||||||
* @brief send a command to Rizin
|
* @brief send a command to Rizin
|
||||||
@ -90,21 +98,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
QString cmd(const char *str);
|
QString cmd(const char *str);
|
||||||
QString cmd(const QString &str) { return cmd(str.toUtf8().constData()); }
|
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
|
* @brief send a task to Rizin
|
||||||
@ -112,6 +105,7 @@ public:
|
|||||||
* @return execute successful?
|
* @return execute successful?
|
||||||
*/
|
*/
|
||||||
bool asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<RizinTask> &task);
|
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
|
* @brief Execute a Rizin command \a cmd. By nature, the API
|
||||||
@ -148,56 +142,41 @@ public:
|
|||||||
return cmdRawAt(str.toUtf8().constData(), address);
|
return cmdRawAt(str.toUtf8().constData(), address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyAtSeek(std::function<void()> fn, RVA address)
|
class SeekReturn
|
||||||
{
|
{
|
||||||
RVA oldOffset = getOffset();
|
RVA returnAddress;
|
||||||
seekSilent(address);
|
bool empty = true;
|
||||||
fn();
|
|
||||||
seekSilent(oldOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
seekSilent(address);
|
||||||
void *ret = fn();
|
return returner;
|
||||||
seekSilent(oldOffset);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CutterJson cmdj(const char *str);
|
CutterJson cmdj(const char *str);
|
||||||
CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }
|
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);
|
QString cmdTask(const QString &str);
|
||||||
CutterJson cmdjTask(const QString &str);
|
|
||||||
/**
|
QString getRizinVersionReadable(const char *program = nullptr);
|
||||||
* @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 getVersionInformation();
|
QString getVersionInformation();
|
||||||
|
|
||||||
CutterJson parseJson(char *res, const char *cmd = nullptr);
|
CutterJson parseJson(char *res, const char *cmd = nullptr);
|
||||||
@ -257,6 +236,7 @@ public:
|
|||||||
void triggerFlagsChanged();
|
void triggerFlagsChanged();
|
||||||
|
|
||||||
/* Edition functions */
|
/* Edition functions */
|
||||||
|
PRzAnalysisBytes getRzAnalysisBytesSingle(RVA addr);
|
||||||
QString getInstructionBytes(RVA addr);
|
QString getInstructionBytes(RVA addr);
|
||||||
QString getInstructionOpcode(RVA addr);
|
QString getInstructionOpcode(RVA addr);
|
||||||
void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);
|
void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);
|
||||||
@ -569,14 +549,12 @@ public:
|
|||||||
bool registerDecompiler(Decompiler *decompiler);
|
bool registerDecompiler(Decompiler *decompiler);
|
||||||
|
|
||||||
RVA getOffsetJump(RVA addr);
|
RVA getOffsetJump(RVA addr);
|
||||||
CutterJson getFileInfo();
|
CutterJson getSignatureInfo();
|
||||||
QString getSignatureInfo();
|
bool existsFileInfo();
|
||||||
CutterJson getFileVersionInfo();
|
|
||||||
void setGraphEmpty(bool empty);
|
void setGraphEmpty(bool empty);
|
||||||
bool isGraphEmpty();
|
bool isGraphEmpty();
|
||||||
|
|
||||||
void getOpcodes();
|
void getRegs();
|
||||||
QList<QString> opcodes;
|
|
||||||
QList<QString> regs;
|
QList<QString> regs;
|
||||||
void setSettings();
|
void setSettings();
|
||||||
|
|
||||||
@ -688,8 +666,6 @@ public:
|
|||||||
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
|
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,
|
||||||
const QString &filterType = QString());
|
const QString &filterType = QString());
|
||||||
|
|
||||||
QList<StringDescription> parseStringsJson(const CutterJson &doc);
|
|
||||||
|
|
||||||
void handleREvent(int type, void *data);
|
void handleREvent(int type, void *data);
|
||||||
|
|
||||||
/* Signals related */
|
/* Signals related */
|
||||||
@ -744,6 +720,25 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool isWriteModeEnabled();
|
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:
|
signals:
|
||||||
void refreshAll();
|
void refreshAll();
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "rz_core.h"
|
#include "rz_core.h"
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include "RizinCpp.h"
|
||||||
|
|
||||||
// Workaround for compile errors on Windows
|
// Workaround for compile errors on Windows
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
@ -14,103 +15,6 @@
|
|||||||
# undef max
|
# undef max
|
||||||
#endif // Q_OS_WIN
|
#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
|
// Global information for Cutter
|
||||||
#define APPNAME "Cutter"
|
#define APPNAME "Cutter"
|
||||||
|
@ -355,8 +355,7 @@ struct RefDescription
|
|||||||
|
|
||||||
struct VariableDescription
|
struct VariableDescription
|
||||||
{
|
{
|
||||||
enum class RefType { SP, BP, Reg };
|
RzAnalysisVarStorageType storageType;
|
||||||
RefType refType;
|
|
||||||
QString name;
|
QString name;
|
||||||
QString type;
|
QString type;
|
||||||
};
|
};
|
||||||
|
@ -70,7 +70,6 @@ public:
|
|||||||
iterator end() const { return iterator(nullptr, nullptr); }
|
iterator end() const { return iterator(nullptr, nullptr); }
|
||||||
|
|
||||||
bool toBool() const { return value && value->type == RZ_JSON_BOOLEAN && value->num.u_value; }
|
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; }
|
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; }
|
ut64 toUt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.u_value : 0; }
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ void MainWindow::initUI()
|
|||||||
&MainWindow::addExtraDisassembly);
|
&MainWindow::addExtraDisassembly);
|
||||||
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
|
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
|
||||||
connect(ui->actionCommitChanges, &QAction::triggered, this,
|
connect(ui->actionCommitChanges, &QAction::triggered, this,
|
||||||
[this]() { Core()->commitWriteCache(); });
|
[]() { Core()->commitWriteCache(); });
|
||||||
ui->actionCommitChanges->setEnabled(false);
|
ui->actionCommitChanges->setEnabled(false);
|
||||||
connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled);
|
connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled);
|
||||||
|
|
||||||
@ -637,7 +637,7 @@ bool MainWindow::openProject(const QString &file)
|
|||||||
|
|
||||||
void MainWindow::finalizeOpen()
|
void MainWindow::finalizeOpen()
|
||||||
{
|
{
|
||||||
core->getOpcodes();
|
core->getRegs();
|
||||||
core->updateSeek();
|
core->updateSeek();
|
||||||
refreshAll();
|
refreshAll();
|
||||||
// Add fortune message
|
// Add fortune message
|
||||||
@ -1086,6 +1086,12 @@ MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA addr
|
|||||||
case MemoryWidgetType::Decompiler:
|
case MemoryWidgetType::Decompiler:
|
||||||
memoryWidget = new DecompilerWidget(this);
|
memoryWidget = new DecompilerWidget(this);
|
||||||
break;
|
break;
|
||||||
|
case MemoryWidgetType::CallGraph:
|
||||||
|
memoryWidget = new CallGraphWidget(this, false);
|
||||||
|
break;
|
||||||
|
case MemoryWidgetType::GlobalCallGraph:
|
||||||
|
memoryWidget = new CallGraphWidget(this, true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
auto seekable = memoryWidget->getSeekable();
|
auto seekable = memoryWidget->getSeekable();
|
||||||
seekable->setSynchronization(synchronized);
|
seekable->setSynchronization(synchronized);
|
||||||
@ -1108,7 +1114,7 @@ void MainWindow::initBackForwardMenu()
|
|||||||
connect(button, &QWidget::customContextMenuRequested, button,
|
connect(button, &QWidget::customContextMenuRequested, button,
|
||||||
[menu, button](const QPoint &pos) { menu->exec(button->mapToGlobal(pos)); });
|
[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
|
// Roughly 10-16 lines depending on padding size, no need to calculate more precisely
|
||||||
menu->setMaximumHeight(metrics.lineSpacing() * 20);
|
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()
|
void MainWindow::on_actionExport_as_code_triggered()
|
||||||
{
|
{
|
||||||
QStringList filters;
|
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)");
|
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)");
|
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)");
|
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)");
|
filters << tr("C uin64_t array (*.c)");
|
||||||
cmdMap[filters.last()] = "pcd";
|
typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_DOUBLEWORDS, big_endian);
|
||||||
filters << tr("C string (*.c)");
|
|
||||||
cmdMap[filters.last()] = "pcs";
|
filters << tr("Go array (*.go)");
|
||||||
filters << tr("Shell-script that reconstructs the bin (*.sh)");
|
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_GOLANG;
|
||||||
cmdMap[filters.last()] = "pcS";
|
filters << tr("Java array (*.java)");
|
||||||
|
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_JAVA;
|
||||||
filters << tr("JSON array (*.json)");
|
filters << tr("JSON array (*.json)");
|
||||||
cmdMap[filters.last()] = "pcj";
|
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_JSON;
|
||||||
filters << tr("JavaScript array (*.js)");
|
filters << tr("Kotlin array (*.kt)");
|
||||||
cmdMap[filters.last()] = "pcJ";
|
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)");
|
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)");
|
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)");
|
filters << tr("GAS .byte blob (*.asm, *.s)");
|
||||||
cmdMap[filters.last()] = "pca";
|
typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_ASM;
|
||||||
filters << tr(".bytes with instructions in comments (*.txt)");
|
|
||||||
cmdMap[filters.last()] = "pcA";
|
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"));
|
QFileDialog dialog(this, tr("Export as code"));
|
||||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||||
@ -1736,13 +1762,25 @@ void MainWindow::on_actionExport_as_code_triggered()
|
|||||||
qWarning() << "Can't open file";
|
qWarning() << "Can't open file";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TempConfig tempConfig;
|
TempConfig tempConfig;
|
||||||
tempConfig.set("io.va", false);
|
tempConfig.set("io.va", false);
|
||||||
QTextStream fileOut(&file);
|
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()
|
void MainWindow::on_actionApplySigFromFile_triggered()
|
||||||
|
2
src/core/RizinCpp.cpp
Normal file
2
src/core/RizinCpp.cpp
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#include "RizinCpp.h"
|
||||||
|
|
168
src/core/RizinCpp.h
Normal file
168
src/core/RizinCpp.h
Normal 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
|
@ -1,4 +1,3 @@
|
|||||||
#include "rz_version.h"
|
|
||||||
#include "core/Cutter.h"
|
#include "core/Cutter.h"
|
||||||
#include "AboutDialog.h"
|
#include "AboutDialog.h"
|
||||||
|
|
||||||
@ -7,7 +6,6 @@
|
|||||||
#include "common/Configuration.h"
|
#include "common/Configuration.h"
|
||||||
#include "common/BugReporting.h"
|
#include "common/BugReporting.h"
|
||||||
|
|
||||||
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
|
@ -86,11 +86,7 @@ bool EditMethodDialog::inputValid()
|
|||||||
|
|
||||||
QString EditMethodDialog::convertRealNameToName(const QString &realName)
|
QString EditMethodDialog::convertRealNameToName(const QString &realName)
|
||||||
{
|
{
|
||||||
std::unique_ptr<const char, void (*)(const char *)> sanitizedCString(
|
return fromOwnedCharPtr(rz_str_sanitize_sdb_key(realName.toUtf8().constData()));
|
||||||
rz_str_sanitize_sdb_key(realName.toUtf8().constData()),
|
|
||||||
[](const char *s) { rz_mem_free((void*)s); });
|
|
||||||
|
|
||||||
return QString(sanitizedCString.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditMethodDialog::setClass(const QString &className)
|
void EditMethodDialog::setClass(const QString &className)
|
||||||
|
@ -22,7 +22,6 @@ TypesInteractionDialog::TypesInteractionDialog(QWidget *parent, bool readOnly)
|
|||||||
syntaxHighLighter = Config()->createSyntaxHighlighter(ui->plainTextEdit->document());
|
syntaxHighLighter = Config()->createSyntaxHighlighter(ui->plainTextEdit->document());
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||||
ui->plainTextEdit->setReadOnly(readOnly);
|
ui->plainTextEdit->setReadOnly(readOnly);
|
||||||
this->typeName = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypesInteractionDialog::~TypesInteractionDialog() {}
|
TypesInteractionDialog::~TypesInteractionDialog() {}
|
||||||
@ -64,10 +63,21 @@ void TypesInteractionDialog::done(int r)
|
|||||||
{
|
{
|
||||||
if (r == QDialog::Accepted) {
|
if (r == QDialog::Accepted) {
|
||||||
RzCoreLocked core(Core());
|
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(),
|
core->analysis->typedb, this->typeName.toUtf8().constData(),
|
||||||
ui->plainTextEdit->toPlainText().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();
|
emit newTypesLoaded();
|
||||||
QDialog::done(r);
|
QDialog::done(r);
|
||||||
return;
|
return;
|
||||||
|
@ -23,145 +23,213 @@ VersionInfoDialog::~VersionInfoDialog() {}
|
|||||||
|
|
||||||
void VersionInfoDialog::fillVersionInfo()
|
void VersionInfoDialog::fillVersionInfo()
|
||||||
{
|
{
|
||||||
|
RzCoreLocked core(Core());
|
||||||
CutterJson doc = Core()->getFileVersionInfo();
|
const RzBinInfo *info = rz_bin_get_info(core->bin);
|
||||||
|
if (!info || !info->rclass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Case ELF
|
// Case ELF
|
||||||
if (doc["verneed"].valid()) {
|
if (strncmp("elf", info->rclass, 3) == 0) {
|
||||||
CutterJson verneed = doc["verneed"].first();
|
|
||||||
CutterJson versym = doc["versym"].first();
|
|
||||||
|
|
||||||
// Set labels
|
// Set labels
|
||||||
ui->leftLabel->setText("Version symbols");
|
ui->leftLabel->setText("Version symbols");
|
||||||
ui->rightLabel->setText("Version need");
|
ui->rightLabel->setText("Version need");
|
||||||
|
|
||||||
// Left tree
|
Sdb *sdb = sdb_ns_path(core->sdb, "bin/cur/info/versioninfo/versym", 0);
|
||||||
QTreeWidgetItem *secNameItemL = new QTreeWidgetItem();
|
if (!sdb) {
|
||||||
secNameItemL->setText(0, "Section name:");
|
return;
|
||||||
secNameItemL->setText(1, versym["section_name"].toString());
|
}
|
||||||
ui->leftTreeWidget->addTopLevelItem(secNameItemL);
|
|
||||||
|
|
||||||
|
// Left tree
|
||||||
QTreeWidgetItem *addrItemL = new QTreeWidgetItem();
|
QTreeWidgetItem *addrItemL = new QTreeWidgetItem();
|
||||||
addrItemL->setText(0, "Address:");
|
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);
|
ui->leftTreeWidget->addTopLevelItem(addrItemL);
|
||||||
|
|
||||||
QTreeWidgetItem *offItemL = new QTreeWidgetItem();
|
QTreeWidgetItem *offItemL = new QTreeWidgetItem();
|
||||||
offItemL->setText(0, "Offset:");
|
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);
|
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();
|
QTreeWidgetItem *entriesItemL = new QTreeWidgetItem();
|
||||||
entriesItemL->setText(0, "Entries:");
|
entriesItemL->setText(0, "Entries:");
|
||||||
for (CutterJson obj : versym["entries"]) {
|
const ut64 num_entries = sdb_num_get(sdb, "num_entries", 0);
|
||||||
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
|
for (size_t i = 0; i < num_entries; ++i) {
|
||||||
tempItem->setText(0, RzAddressString(obj["idx"].toRVA()));
|
auto key = QString("entry%0").arg(i);
|
||||||
tempItem->setText(1, obj["value"].toString());
|
const char *const value = sdb_const_get(sdb, key.toStdString().c_str(), 0);
|
||||||
entriesItemL->addChild(tempItem);
|
if (!value) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto item = new QTreeWidgetItem();
|
||||||
|
item->setText(0, RzAddressString(i));
|
||||||
|
item->setText(1, value);
|
||||||
|
entriesItemL->addChild(item);
|
||||||
}
|
}
|
||||||
ui->leftTreeWidget->addTopLevelItem(entriesItemL);
|
ui->leftTreeWidget->addTopLevelItem(entriesItemL);
|
||||||
|
|
||||||
// Adjust columns to content
|
// Adjust columns to content
|
||||||
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
|
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
|
||||||
|
sdb = sdb_ns_path(core->sdb, "bin/cur/info/versioninfo/verneed", 0);
|
||||||
|
|
||||||
// Right tree
|
// 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();
|
QTreeWidgetItem *addrItemR = new QTreeWidgetItem();
|
||||||
addrItemR->setText(0, "Address:");
|
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);
|
ui->rightTreeWidget->addTopLevelItem(addrItemR);
|
||||||
|
|
||||||
QTreeWidgetItem *offItemR = new QTreeWidgetItem();
|
QTreeWidgetItem *offItemR = new QTreeWidgetItem();
|
||||||
offItemR->setText(0, "Offset:");
|
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);
|
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();
|
QTreeWidgetItem *entriesItemR = new QTreeWidgetItem();
|
||||||
entriesItemR->setText(0, "Entries:");
|
entriesItemR->setText(0, "Entries:");
|
||||||
for (CutterJson parentObj : verneed["entries"]) {
|
for (size_t num_version = 0;; num_version++) {
|
||||||
QTreeWidgetItem *parentItem = new QTreeWidgetItem();
|
auto path_version =
|
||||||
QString parentString;
|
QString("bin/cur/info/versioninfo/verneed/version%0").arg(num_version);
|
||||||
parentItem->setText(0, RzAddressString(parentObj["idx"].toRVA()));
|
sdb = sdb_ns_path(core->sdb, path_version.toStdString().c_str(), 0);
|
||||||
parentString.append("Version: " + QString::number(parentObj["vn_version"].toSt64())
|
if (!sdb) {
|
||||||
+ "\t");
|
break;
|
||||||
parentString.append("File: " + parentObj["file_name"].toString());
|
}
|
||||||
parentItem->setText(1, parentString);
|
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"]) {
|
int num_vernaux = 0;
|
||||||
QTreeWidgetItem *childItem = new QTreeWidgetItem();
|
while (true) {
|
||||||
QString childString;
|
auto path_vernaux =
|
||||||
childItem->setText(0, RzAddressString(childObj["idx"].toRVA()));
|
QString("%0/vernaux%1").arg(path_version, QString::number(num_vernaux++));
|
||||||
childString.append("Name: " + childObj["name"].toString() + "\t");
|
sdb = sdb_ns_path(core->sdb, path_vernaux.toStdString().c_str(), 0);
|
||||||
childString.append("Flags: " + childObj["flags"].toString() + "\t");
|
if (!sdb) {
|
||||||
childString.append("Version: " + QString::number(childObj["version"].toSt64()));
|
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);
|
childItem->setText(1, childString);
|
||||||
parentItem->addChild(childItem);
|
parentItem->addChild(childItem);
|
||||||
}
|
}
|
||||||
entriesItemR->addChild(parentItem);
|
entriesItemR->addChild(parentItem);
|
||||||
}
|
}
|
||||||
ui->rightTreeWidget->addTopLevelItem(entriesItemR);
|
|
||||||
|
|
||||||
|
ui->rightTreeWidget->addTopLevelItem(entriesItemR);
|
||||||
// Adjust columns to content
|
// Adjust columns to content
|
||||||
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
|
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case PE
|
// Case PE
|
||||||
else if (doc["VS_FIXEDFILEINFO"].valid()) {
|
else if (strncmp("pe", info->rclass, 2) == 0) {
|
||||||
CutterJson vs = doc["VS_FIXEDFILEINFO"];
|
|
||||||
CutterJson strings = doc["StringTable"];
|
|
||||||
|
|
||||||
// Set labels
|
// Set labels
|
||||||
ui->leftLabel->setText("VS Fixed file info");
|
ui->leftLabel->setText("VS Fixed file info");
|
||||||
ui->rightLabel->setText("String table");
|
ui->rightLabel->setText("String table");
|
||||||
|
Sdb *sdb = NULL;
|
||||||
|
|
||||||
// Left tree
|
// Left tree
|
||||||
for (CutterJson property : vs) {
|
auto path_version = QString("bin/cur/info/vs_version_info/VS_VERSIONINFO%0").arg(0);
|
||||||
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
|
auto path_fixedfileinfo = QString("%0/fixed_file_info").arg(path_version);
|
||||||
tempItem->setText(0, property.key());
|
sdb = sdb_ns_path(core->sdb, path_fixedfileinfo.toStdString().c_str(), 0);
|
||||||
if (property.type() == RZ_JSON_INTEGER)
|
if (!sdb) {
|
||||||
tempItem->setText(1, RzHexString(property.toRVA()));
|
return;
|
||||||
else
|
|
||||||
tempItem->setText(1, property.toString());
|
|
||||||
ui->leftTreeWidget->addTopLevelItem(tempItem);
|
|
||||||
|
|
||||||
// Adjust columns to content
|
|
||||||
qhelpers::adjustColumns(ui->leftTreeWidget, 0);
|
|
||||||
}
|
}
|
||||||
|
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
|
// Right tree
|
||||||
for (CutterJson property : strings) {
|
for (int num_stringtable = 0;; num_stringtable++) {
|
||||||
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
|
auto path_stringtable = QString("%0/string_file_info/stringtable%1")
|
||||||
tempItem->setText(0, property.key());
|
.arg(path_version)
|
||||||
tempItem->setText(1, property.toString());
|
.arg(num_stringtable);
|
||||||
ui->rightTreeWidget->addTopLevelItem(tempItem);
|
sdb = sdb_ns_path(core->sdb, path_stringtable.toStdString().c_str(), 0);
|
||||||
|
if (!sdb) {
|
||||||
// Adjust columns to content
|
break;
|
||||||
qhelpers::adjustColumns(ui->rightTreeWidget, 0);
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,8 +131,18 @@ void XrefsDialog::updatePreview(RVA addr)
|
|||||||
tempConfig.set("asm.lines", false);
|
tempConfig.set("asm.lines", false);
|
||||||
tempConfig.set("asm.bytes", false);
|
tempConfig.set("asm.bytes", false);
|
||||||
|
|
||||||
// Use cmd because cmRaw cannot handle the output properly. Why?
|
QString disas = Core()->getFunctionExecOut(
|
||||||
QString disas = Core()->cmd("pd--20 @ " + QString::number(addr));
|
[](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);
|
ui->previewTextEdit->document()->setHtml(disas);
|
||||||
|
|
||||||
// Does it make any sense?
|
// Does it make any sense?
|
||||||
|
@ -69,6 +69,11 @@ DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWi
|
|||||||
|
|
||||||
DecompilerContextMenu::~DecompilerContextMenu() {}
|
DecompilerContextMenu::~DecompilerContextMenu() {}
|
||||||
|
|
||||||
|
QWidget *DecompilerContextMenu::parentForDialog()
|
||||||
|
{
|
||||||
|
return parentWidget();
|
||||||
|
}
|
||||||
|
|
||||||
void DecompilerContextMenu::setAnnotationHere(RzCodeAnnotation *annotation)
|
void DecompilerContextMenu::setAnnotationHere(RzCodeAnnotation *annotation)
|
||||||
{
|
{
|
||||||
annotationHere = annotation;
|
annotationHere = annotation;
|
||||||
@ -404,14 +409,15 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
|
|||||||
RzAnalysisFunction *func = Core()->functionAt(func_addr);
|
RzAnalysisFunction *func = Core()->functionAt(func_addr);
|
||||||
if (func == NULL) {
|
if (func == NULL) {
|
||||||
QString function_name = QInputDialog::getText(
|
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);
|
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
|
||||||
if (ok && !function_name.isEmpty()) {
|
if (ok && !function_name.isEmpty()) {
|
||||||
Core()->createFunctionAt(func_addr, function_name);
|
Core()->createFunctionAt(func_addr, function_name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QString newName = QInputDialog::getText(
|
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);
|
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
|
||||||
if (ok && !newName.isEmpty()) {
|
if (ok && !newName.isEmpty()) {
|
||||||
Core()->renameFunction(func_addr, newName);
|
Core()->renameFunction(func_addr, newName);
|
||||||
@ -421,16 +427,16 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
|
|||||||
RVA var_addr = annotationHere->reference.offset;
|
RVA var_addr = annotationHere->reference.offset;
|
||||||
RzFlagItem *flagDetails = rz_flag_get_i(core->flags, var_addr);
|
RzFlagItem *flagDetails = rz_flag_get_i(core->flags, var_addr);
|
||||||
if (flagDetails) {
|
if (flagDetails) {
|
||||||
QString newName = QInputDialog::getText(this, tr("Rename %2").arg(flagDetails->name),
|
QString newName = QInputDialog::getText(
|
||||||
tr("Enter name"), QLineEdit::Normal,
|
parentForDialog(), tr("Rename %2").arg(flagDetails->name), tr("Enter name"),
|
||||||
flagDetails->name, &ok);
|
QLineEdit::Normal, flagDetails->name, &ok);
|
||||||
if (ok && !newName.isEmpty()) {
|
if (ok && !newName.isEmpty()) {
|
||||||
Core()->renameFlag(flagDetails->name, newName);
|
Core()->renameFlag(flagDetails->name, newName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QString newName = QInputDialog::getText(
|
QString newName = QInputDialog::getText(
|
||||||
this, tr("Add name to %2").arg(curHighlightedWord), tr("Enter name"),
|
parentForDialog(), tr("Add name to %2").arg(curHighlightedWord),
|
||||||
QLineEdit::Normal, curHighlightedWord, &ok);
|
tr("Enter name"), QLineEdit::Normal, curHighlightedWord, &ok);
|
||||||
if (ok && !newName.isEmpty()) {
|
if (ok && !newName.isEmpty()) {
|
||||||
Core()->addFlag(var_addr, newName, 1);
|
Core()->addFlag(var_addr, newName, 1);
|
||||||
}
|
}
|
||||||
@ -439,14 +445,14 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
|
|||||||
if (!variablePresentInRizin()) {
|
if (!variablePresentInRizin()) {
|
||||||
// Show can't rename this variable dialog
|
// Show can't rename this variable dialog
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
this,
|
parentForDialog(),
|
||||||
tr("Rename local variable %1").arg(QString(annotationHere->variable.name)),
|
tr("Rename local variable %1").arg(QString(annotationHere->variable.name)),
|
||||||
tr("Can't rename this variable. "
|
tr("Can't rename this variable. "
|
||||||
"Only local variables defined in disassembly can be renamed."));
|
"Only local variables defined in disassembly can be renamed."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString oldName(annotationHere->variable.name);
|
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);
|
tr("Enter name"), QLineEdit::Normal, oldName, &ok);
|
||||||
if (ok && !newName.isEmpty()) {
|
if (ok && !newName.isEmpty()) {
|
||||||
Core()->renameFunctionVariable(newName, oldName, decompiledFunctionAddress);
|
Core()->renameFunctionVariable(newName, oldName, decompiledFunctionAddress);
|
||||||
@ -465,13 +471,14 @@ void DecompilerContextMenu::actionEditFunctionVariablesTriggered()
|
|||||||
return;
|
return;
|
||||||
} else if (!variablePresentInRizin()) {
|
} else if (!variablePresentInRizin()) {
|
||||||
QMessageBox::critical(
|
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. "
|
tr("Can't edit this variable. "
|
||||||
"Only local variables defined in disassembly can be edited."));
|
"Only local variables defined in disassembly can be edited."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EditVariablesDialog dialog(decompiledFunctionAddress, QString(annotationHere->variable.name),
|
EditVariablesDialog dialog(decompiledFunctionAddress, QString(annotationHere->variable.name),
|
||||||
this);
|
parentForDialog());
|
||||||
dialog.exec();
|
dialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +111,12 @@ private:
|
|||||||
QAction actionSetPC;
|
QAction actionSetPC;
|
||||||
|
|
||||||
// Private Functions
|
// 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
|
* @brief Sets the shortcut context in all the actions contained
|
||||||
* in the specified QMenu to Qt::WidgetWithChildrenShortcut.
|
* in the specified QMenu to Qt::WidgetWithChildrenShortcut.
|
||||||
|
@ -313,34 +313,33 @@ void DisassemblyContextMenu::addDebugMenu()
|
|||||||
|
|
||||||
QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
|
QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
|
||||||
{
|
{
|
||||||
QVector<ThingUsedHere> result;
|
RzCoreLocked core(Core());
|
||||||
const CutterJson array = Core()->cmdj("anj @ " + QString::number(offset));
|
auto p = fromOwned(
|
||||||
result.reserve(array.size());
|
rz_core_analysis_name(core, offset), rz_core_analysis_name_free);
|
||||||
for (const auto &thing : array) {
|
if (!p) {
|
||||||
auto obj = thing;
|
return {};
|
||||||
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 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,13 +481,7 @@ void DisassemblyContextMenu::setupRenaming()
|
|||||||
void DisassemblyContextMenu::aboutToShowSlot()
|
void DisassemblyContextMenu::aboutToShowSlot()
|
||||||
{
|
{
|
||||||
// check if set immediate base menu makes sense
|
// check if set immediate base menu makes sense
|
||||||
RzPVector *vec = (RzPVector *)Core()->returnAtSeek(
|
auto ab = Core()->getRzAnalysisBytesSingle(offset);
|
||||||
[&]() {
|
|
||||||
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));
|
|
||||||
|
|
||||||
bool immBase = ab && ab->op && (ab->op->val || ab->op->ptr);
|
bool immBase = ab && ab->op && (ab->op->val || ab->op->ptr);
|
||||||
setBaseMenu->menuAction()->setVisible(immBase);
|
setBaseMenu->menuAction()->setVisible(immBase);
|
||||||
@ -514,7 +507,6 @@ void DisassemblyContextMenu::aboutToShowSlot()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rz_pvector_free(vec);
|
|
||||||
|
|
||||||
if (memBaseReg.isEmpty()) {
|
if (memBaseReg.isEmpty()) {
|
||||||
// hide structure offset menu
|
// hide structure offset menu
|
||||||
@ -532,7 +524,7 @@ void DisassemblyContextMenu::aboutToShowSlot()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
structureOffsetMenu->addAction("[" + memBaseReg + " + " + ty->path + "]")
|
structureOffsetMenu->addAction("[" + memBaseReg + " + " + ty->path + "]")
|
||||||
->setData(ty->path);
|
->setData(QString(ty->path));
|
||||||
}
|
}
|
||||||
rz_list_free(typeoffs);
|
rz_list_free(typeoffs);
|
||||||
}
|
}
|
||||||
@ -726,18 +718,13 @@ void DisassemblyContextMenu::on_actionNopInstruction_triggered()
|
|||||||
|
|
||||||
void DisassemblyContextMenu::showReverseJmpQuery()
|
void DisassemblyContextMenu::showReverseJmpQuery()
|
||||||
{
|
{
|
||||||
QString type;
|
actionJmpReverse.setVisible(false);
|
||||||
|
auto ab = Core()->getRzAnalysisBytesSingle(offset);
|
||||||
CutterJson array = Core()->cmdj("pdj 1 @ " + RzAddressString(offset));
|
if (!(ab && ab->op)) {
|
||||||
if (!array.size()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (ab->op->type == RZ_ANALYSIS_OP_TYPE_CJMP) {
|
||||||
type = array.first()["type"].toString();
|
|
||||||
if (type == "cjmp") {
|
|
||||||
actionJmpReverse.setVisible(true);
|
actionJmpReverse.setVisible(true);
|
||||||
} else {
|
|
||||||
actionJmpReverse.setVisible(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1036,18 +1023,15 @@ void DisassemblyContextMenu::on_actionEditFunction_triggered()
|
|||||||
|
|
||||||
if (dialog.exec()) {
|
if (dialog.exec()) {
|
||||||
QString new_name = dialog.getNameText();
|
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();
|
QString new_start_addr = dialog.getStartAddrText();
|
||||||
fcn->addr = Core()->math(new_start_addr);
|
fcn->addr = Core()->math(new_start_addr);
|
||||||
QString new_stack_size = dialog.getStackSizeText();
|
QString new_stack_size = dialog.getStackSizeText();
|
||||||
fcn->stack = int(Core()->math(new_stack_size));
|
fcn->stack = int(Core()->math(new_stack_size));
|
||||||
|
|
||||||
const char *ccSelected = dialog.getCallConSelected().toUtf8().constData();
|
QByteArray newCC = dialog.getCallConSelected().toUtf8();
|
||||||
if (RZ_STR_ISEMPTY(ccSelected)) {
|
if (!newCC.isEmpty() && rz_analysis_cc_exist(core->analysis, newCC.constData())) {
|
||||||
return;
|
fcn->cc = rz_str_constpool_get(&core->analysis->constpool, newCC.constData());
|
||||||
}
|
|
||||||
if (rz_analysis_cc_exist(core->analysis, ccSelected)) {
|
|
||||||
fcn->cc = rz_str_constpool_get(&core->analysis->constpool, ccSelected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Core()->functionsChanged();
|
emit Core()->functionsChanged();
|
||||||
|
@ -133,6 +133,10 @@ QString PluginManager::getUserPluginsDirectory() const
|
|||||||
void PluginManager::loadNativePlugins(const QDir &directory)
|
void PluginManager::loadNativePlugins(const QDir &directory)
|
||||||
{
|
{
|
||||||
for (const QString &fileName : directory.entryList(QDir::Files)) {
|
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));
|
QPluginLoader pluginLoader(directory.absoluteFilePath(fileName));
|
||||||
QObject *plugin = pluginLoader.instance();
|
QObject *plugin = pluginLoader.instance();
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.12)
|
|||||||
project(cutter-sample-plugin)
|
project(cutter-sample-plugin)
|
||||||
|
|
||||||
find_package(Cutter REQUIRED)
|
find_package(Cutter REQUIRED)
|
||||||
|
find_package(Rizin REQUIRED)
|
||||||
set(CUTTER_INSTALL_PLUGDIR "${Cutter_USER_PLUGINDIR}" CACHE STRING "Directory to install Cutter plugin into")
|
set(CUTTER_INSTALL_PLUGDIR "${Cutter_USER_PLUGINDIR}" CACHE STRING "Directory to install Cutter plugin into")
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
@ -9,5 +10,5 @@ set(CMAKE_AUTOMOC ON)
|
|||||||
add_library(sample_plugin MODULE
|
add_library(sample_plugin MODULE
|
||||||
CutterSamplePlugin.h
|
CutterSamplePlugin.h
|
||||||
CutterSamplePlugin.cpp)
|
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}")
|
install(TARGETS sample_plugin DESTINATION "${CUTTER_INSTALL_PLUGDIR}")
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <common/TempConfig.h>
|
#include <common/TempConfig.h>
|
||||||
#include <common/Configuration.h>
|
#include <common/Configuration.h>
|
||||||
#include <MainWindow.h>
|
#include <MainWindow.h>
|
||||||
|
#include <librz/rz_core.h>
|
||||||
|
|
||||||
void CutterSamplePlugin::setupPlugin() {}
|
void CutterSamplePlugin::setupPlugin() {}
|
||||||
|
|
||||||
@ -46,25 +47,21 @@ CutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main) : CutterDoc
|
|||||||
void CutterSamplePluginWidget::on_seekChanged(RVA addr)
|
void CutterSamplePluginWidget::on_seekChanged(RVA addr)
|
||||||
{
|
{
|
||||||
Q_UNUSED(addr);
|
Q_UNUSED(addr);
|
||||||
QString res;
|
RzCoreLocked core(Core());
|
||||||
{
|
TempConfig tempConfig;
|
||||||
TempConfig tempConfig;
|
tempConfig.set("scr.color", 0);
|
||||||
tempConfig.set("scr.color", 0);
|
QString disasm = Core()->disassembleSingleInstruction(Core()->getOffset());
|
||||||
res = Core()->cmd("?E `pi 1`");
|
QString res = fromOwnedCharPtr(rz_core_clippy(core, disasm.toUtf8().constData()));
|
||||||
}
|
|
||||||
text->setText(res);
|
text->setText(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CutterSamplePluginWidget::on_buttonClicked()
|
void CutterSamplePluginWidget::on_buttonClicked()
|
||||||
{
|
{
|
||||||
RzCoreLocked core(Core());
|
RzCoreLocked core(Core());
|
||||||
char *fortune = rz_core_fortune_get_random(core);
|
auto fortune = fromOwned(rz_core_fortune_get_random(core));
|
||||||
if (!fortune) {
|
if (!fortune) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// cmdRaw can be used to execute single raw commands
|
QString res = fromOwnedCharPtr(rz_core_clippy(core, fortune.get()));
|
||||||
// this is especially good for user-controlled input
|
|
||||||
QString res = Core()->cmdRaw("?E " + QString::fromUtf8(fortune));
|
|
||||||
text->setText(res);
|
text->setText(res);
|
||||||
rz_mem_free(fortune);
|
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class FortuneWidget(cutter.CutterDockWidget):
|
|||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def generate_fortune(self):
|
def generate_fortune(self):
|
||||||
fortune = cutter.cmd("fo").replace("\n", "")
|
fortune = cutter.cmd("fortune").replace("\n", "")
|
||||||
res = cutter.core().cmdRaw(f"?E {fortune}")
|
res = cutter.core().cmdRaw(f"?E {fortune}")
|
||||||
self.text.setText(res)
|
self.text.setText(res)
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ class FortuneWidget(cutter.CutterDockWidget):
|
|||||||
class CutterSamplePlugin(cutter.CutterPlugin):
|
class CutterSamplePlugin(cutter.CutterPlugin):
|
||||||
name = "Sample Plugin"
|
name = "Sample Plugin"
|
||||||
description = "A sample plugin written in python."
|
description = "A sample plugin written in python."
|
||||||
version = "1.1"
|
version = "1.2"
|
||||||
author = "Cutter developers"
|
author = "Cutter developers"
|
||||||
|
|
||||||
# Override CutterPlugin methods
|
# Override CutterPlugin methods
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 974298653ba71b958e1b6c83f6011f5fefff6236
|
Subproject commit 41c0c778b942577749ea2fed117e48a2cf3892df
|
@ -7,9 +7,11 @@
|
|||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
CallGraphWidget::CallGraphWidget(MainWindow *main, bool global)
|
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());
|
this->setWindowTitle(getWindowTitle());
|
||||||
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &CallGraphWidget::onSeekChanged);
|
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &CallGraphWidget::onSeekChanged);
|
||||||
|
|
||||||
@ -23,6 +25,11 @@ QString CallGraphWidget::getWindowTitle() const
|
|||||||
return global ? tr("Global Callgraph") : tr("Callgraph");
|
return global ? tr("Global Callgraph") : tr("Callgraph");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CallGraphWidget::getWidgetType() const
|
||||||
|
{
|
||||||
|
return global ? tr("GlobalCallgraph") : tr("Callgraph");
|
||||||
|
}
|
||||||
|
|
||||||
void CallGraphWidget::onSeekChanged(RVA address)
|
void CallGraphWidget::onSeekChanged(RVA address)
|
||||||
{
|
{
|
||||||
if (auto function = Core()->functionIn(address)) {
|
if (auto function = Core()->functionIn(address)) {
|
||||||
@ -37,6 +44,7 @@ CallGraphView::CallGraphView(CutterDockWidget *parent, MainWindow *main, bool gl
|
|||||||
refreshDeferrer.registerFor(parent);
|
refreshDeferrer.registerFor(parent);
|
||||||
connect(&refreshDeferrer, &RefreshDeferrer::refreshNow, this, &CallGraphView::refreshView);
|
connect(&refreshDeferrer, &RefreshDeferrer::refreshNow, this, &CallGraphView::refreshView);
|
||||||
connect(Core(), &CutterCore::refreshAll, this, &SimpleTextGraphView::refreshView);
|
connect(Core(), &CutterCore::refreshAll, this, &SimpleTextGraphView::refreshView);
|
||||||
|
connect(Core(), &CutterCore::functionRenamed, this, &CallGraphView::refreshView);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CallGraphView::showExportDialog()
|
void CallGraphView::showExportDialog()
|
||||||
@ -47,17 +55,14 @@ void CallGraphView::showExportDialog()
|
|||||||
} else {
|
} else {
|
||||||
defaultName = QString("callgraph_%1").arg(RzAddressString(address));
|
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)
|
void CallGraphView::showAddress(RVA address)
|
||||||
{
|
{
|
||||||
if (global) {
|
if (global) {
|
||||||
auto addressMappingIt = addressMapping.find(address);
|
selectBlockWithId(address);
|
||||||
if (addressMappingIt != addressMapping.end()) {
|
showBlock(blocks[address]);
|
||||||
selectBlockWithId(addressMappingIt->second);
|
|
||||||
showBlock(blocks[addressMappingIt->second]);
|
|
||||||
}
|
|
||||||
} else if (address != this->address) {
|
} else if (address != this->address) {
|
||||||
this->address = address;
|
this->address = address;
|
||||||
refreshView();
|
refreshView();
|
||||||
@ -72,53 +77,70 @@ void CallGraphView::refreshView()
|
|||||||
SimpleTextGraphView::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()
|
void CallGraphView::loadCurrentGraph()
|
||||||
{
|
{
|
||||||
blockContent.clear();
|
blockContent.clear();
|
||||||
blocks.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 xrefs = fromOwned(rz_analysis_function_get_xrefs_from(fcn));
|
||||||
auto nextId = idMapping.size();
|
auto calls = std::unordered_set<ut64>();
|
||||||
auto &itemId = idMapping[name];
|
for (const auto &xref : CutterRzList<RzAnalysisXRef>(xrefs.get())) {
|
||||||
if (idMapping.size() != nextId) {
|
const auto x = xref->to;
|
||||||
itemId = nextId;
|
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) {
|
if (global) {
|
||||||
QString name = block["name"].toString();
|
for (const auto &fcn : CutterRzList<RzAnalysisFunction>(Core()->core()->analysis->fcns)) {
|
||||||
|
if (!isBetween(from, fcn->addr, to)) {
|
||||||
auto edges = block["imports"];
|
continue;
|
||||||
GraphLayout::GraphBlock layoutBlock;
|
}
|
||||||
layoutBlock.entry = getId(name);
|
addFunction(fcn);
|
||||||
for (auto edge : edges) {
|
}
|
||||||
auto targetName = edge.toString();
|
} else {
|
||||||
auto targetId = getId(targetName);
|
const auto &fcn = Core()->functionIn(address);
|
||||||
layoutBlock.edges.emplace_back(targetId);
|
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()) {
|
for (const auto &x : edges) {
|
||||||
GraphLayout::GraphBlock block;
|
if (blockContent.find(x) != blockContent.end()) {
|
||||||
block.entry = it.value();
|
continue;
|
||||||
addBlock(std::move(block), it.key(), Core()->num(it.key()));
|
|
||||||
}
|
}
|
||||||
|
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) {
|
if (blockContent.empty() && !global) {
|
||||||
addBlock({}, RzAddressString(address), address);
|
const auto name = RzAddressString(address);
|
||||||
}
|
addBlock({}, name, address);
|
||||||
|
|
||||||
addressMapping.clear();
|
|
||||||
for (auto &it : blockContent) {
|
|
||||||
addressMapping[it.second.address] = it.first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
computeGraphPlacement();
|
computeGraphPlacement();
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define CALL_GRAPH_WIDGET_H
|
#define CALL_GRAPH_WIDGET_H
|
||||||
|
|
||||||
#include "core/Cutter.h"
|
#include "core/Cutter.h"
|
||||||
#include "AddressableDockWidget.h"
|
#include "MemoryDockWidget.h"
|
||||||
#include "widgets/SimpleTextGraphView.h"
|
#include "widgets/SimpleTextGraphView.h"
|
||||||
#include "common/RefreshDeferrer.h"
|
#include "common/RefreshDeferrer.h"
|
||||||
|
|
||||||
@ -22,7 +22,6 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
bool global; ///< is this a global or function callgraph
|
bool global; ///< is this a global or function callgraph
|
||||||
RVA address = RVA_INVALID; ///< function address if this is not a global 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 loadCurrentGraph() override;
|
||||||
void restoreCurrentBlock() override;
|
void restoreCurrentBlock() override;
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ private:
|
|||||||
RVA lastLoadedAddress = RVA_INVALID;
|
RVA lastLoadedAddress = RVA_INVALID;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CallGraphWidget : public AddressableDockWidget
|
class CallGraphWidget : public MemoryDockWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -39,6 +38,8 @@ public:
|
|||||||
explicit CallGraphWidget(MainWindow *main, bool global);
|
explicit CallGraphWidget(MainWindow *main, bool global);
|
||||||
~CallGraphWidget();
|
~CallGraphWidget();
|
||||||
|
|
||||||
|
QString getWidgetType() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString getWindowTitle() const override;
|
QString getWindowTitle() const override;
|
||||||
|
|
||||||
|
@ -48,8 +48,9 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
|
|||||||
|
|
||||||
ColorOption currCO = index.data(Qt::UserRole).value<ColorOption>();
|
ColorOption currCO = index.data(Qt::UserRole).value<ColorOption>();
|
||||||
|
|
||||||
|
QFontMetrics fm = QFontMetrics(painter->font());
|
||||||
int penWidth = painter->pen().width();
|
int penWidth = painter->pen().width();
|
||||||
int fontHeight = painter->fontMetrics().height();
|
int fontHeight = fm.height();
|
||||||
QPoint tl = option.rect.topLeft();
|
QPoint tl = option.rect.topLeft();
|
||||||
|
|
||||||
QRect optionNameRect;
|
QRect optionNameRect;
|
||||||
@ -126,9 +127,9 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
|
|||||||
|
|
||||||
painter->setPen(qApp->palette().text().color());
|
painter->setPen(qApp->palette().text().color());
|
||||||
|
|
||||||
QString name =
|
QFontMetrics fm2 = QFontMetrics(painter->font());
|
||||||
painter->fontMetrics().elidedText(optionInfoMap__[currCO.optionName].displayingtext,
|
QString name = fm2.elidedText(optionInfoMap__[currCO.optionName].displayingtext,
|
||||||
Qt::ElideRight, optionNameRect.width());
|
Qt::ElideRight, optionNameRect.width());
|
||||||
painter->drawText(optionNameRect, name);
|
painter->drawText(optionNameRect, name);
|
||||||
|
|
||||||
QPainterPath roundedOptionRect;
|
QPainterPath roundedOptionRect;
|
||||||
@ -155,7 +156,8 @@ void ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o
|
|||||||
painter->setPen(currCO.color);
|
painter->setPen(currCO.color);
|
||||||
painter->fillPath(roundedColorRect, 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,
|
currCO.optionName + ": " + optionInfoMap__[currCO.optionName].info, Qt::ElideRight,
|
||||||
descTextRect.width());
|
descTextRect.width());
|
||||||
painter->setPen(qApp->palette().text().color());
|
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()));
|
data.replace(QRegularExpression("#[0-9a-fA-F]{6}"), QString("%1").arg(after.name()));
|
||||||
|
|
||||||
QSvgRenderer svgRenderer(data.toUtf8());
|
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);
|
pix.fill(Qt::transparent);
|
||||||
|
|
||||||
QPainter pixPainter(&pix);
|
QPainter pixPainter(&pix);
|
||||||
|
@ -6,8 +6,14 @@ ComboQuickFilterView::ComboQuickFilterView(QWidget *parent)
|
|||||||
{
|
{
|
||||||
ui->setupUi(this);
|
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,
|
connect(ui->lineEdit, &QLineEdit::textChanged, this,
|
||||||
[this](const QString &text) { emit filterTextChanged(text); });
|
[this](const QString &text) { debounceTimer->start(150); });
|
||||||
}
|
}
|
||||||
|
|
||||||
ComboQuickFilterView::~ComboQuickFilterView()
|
ComboQuickFilterView::~ComboQuickFilterView()
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ComboQuickFilterView;
|
class ComboQuickFilterView;
|
||||||
@ -32,6 +33,7 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ComboQuickFilterView *ui;
|
Ui::ComboQuickFilterView *ui;
|
||||||
|
QTimer *debounceTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // COMBOQUICKFILTERVIEW_H
|
#endif // COMBOQUICKFILTERVIEW_H
|
||||||
|
@ -152,7 +152,7 @@ void CutterGraphView::zoomReset()
|
|||||||
|
|
||||||
void CutterGraphView::showExportDialog()
|
void CutterGraphView::showExportDialog()
|
||||||
{
|
{
|
||||||
showExportGraphDialog("graph", "", RVA_INVALID);
|
showExportGraphDialog("global_funcall", RZ_CORE_GRAPH_TYPE_FUNCALL, RVA_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CutterGraphView::updateColors()
|
void CutterGraphView::updateColors()
|
||||||
@ -318,12 +318,12 @@ void CutterGraphView::mouseMoveEvent(QMouseEvent *event)
|
|||||||
emit graphMoved();
|
emit graphMoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CutterGraphView::exportGraph(QString filePath, GraphExportType type, QString graphCommand,
|
void CutterGraphView::exportGraph(QString filePath, GraphExportType exportType,
|
||||||
RVA address)
|
RzCoreGraphType graphType, RVA address)
|
||||||
{
|
{
|
||||||
bool graphTransparent = Config()->getBitmapTransparentState();
|
bool graphTransparent = Config()->getBitmapTransparentState();
|
||||||
double graphScaleFactor = Config()->getBitmapExportScaleFactor();
|
double graphScaleFactor = Config()->getBitmapExportScaleFactor();
|
||||||
switch (type) {
|
switch (exportType) {
|
||||||
case GraphExportType::Png:
|
case GraphExportType::Png:
|
||||||
this->saveAsBitmap(filePath, "png", graphScaleFactor, graphTransparent);
|
this->saveAsBitmap(filePath, "png", graphScaleFactor, graphTransparent);
|
||||||
break;
|
break;
|
||||||
@ -335,56 +335,55 @@ void CutterGraphView::exportGraph(QString filePath, GraphExportType type, QStrin
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case GraphExportType::GVDot:
|
case GraphExportType::GVDot:
|
||||||
exportRzTextGraph(filePath, graphCommand + "d", address);
|
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_DOT, address);
|
||||||
break;
|
break;
|
||||||
case GraphExportType::RzJson:
|
case GraphExportType::RzJson:
|
||||||
exportRzTextGraph(filePath, graphCommand + "j", address);
|
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_JSON, address);
|
||||||
break;
|
break;
|
||||||
case GraphExportType::RzGml:
|
case GraphExportType::RzGml:
|
||||||
exportRzTextGraph(filePath, graphCommand + "g", address);
|
exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_GML, address);
|
||||||
break;
|
|
||||||
case GraphExportType::RzSDBKeyValue:
|
|
||||||
exportRzTextGraph(filePath, graphCommand + "k", address);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GraphExportType::GVJson:
|
case GraphExportType::GVJson:
|
||||||
exportRizinGraphvizGraph(filePath, "json", graphCommand, address);
|
Core()->writeGraphvizGraphToFile(filePath, "json", graphType, address);
|
||||||
break;
|
break;
|
||||||
case GraphExportType::GVGif:
|
case GraphExportType::GVGif:
|
||||||
exportRizinGraphvizGraph(filePath, "gif", graphCommand, address);
|
Core()->writeGraphvizGraphToFile(filePath, "gif", graphType, address);
|
||||||
break;
|
break;
|
||||||
case GraphExportType::GVPng:
|
case GraphExportType::GVPng:
|
||||||
exportRizinGraphvizGraph(filePath, "png", graphCommand, address);
|
Core()->writeGraphvizGraphToFile(filePath, "png", graphType, address);
|
||||||
break;
|
break;
|
||||||
case GraphExportType::GVJpeg:
|
case GraphExportType::GVJpeg:
|
||||||
exportRizinGraphvizGraph(filePath, "jpg", graphCommand, address);
|
Core()->writeGraphvizGraphToFile(filePath, "jpg", graphType, address);
|
||||||
break;
|
break;
|
||||||
case GraphExportType::GVPostScript:
|
case GraphExportType::GVPostScript:
|
||||||
exportRizinGraphvizGraph(filePath, "ps", graphCommand, address);
|
Core()->writeGraphvizGraphToFile(filePath, "ps", graphType, address);
|
||||||
break;
|
break;
|
||||||
case GraphExportType::GVSvg:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CutterGraphView::exportRizinGraphvizGraph(QString filePath, QString type, QString graphCommand,
|
void CutterGraphView::exportRzTextGraph(QString filePath, RzCoreGraphType type,
|
||||||
RVA address)
|
RzCoreGraphFormat format, RVA address)
|
||||||
{
|
{
|
||||||
TempConfig tempConfig;
|
char *string = Core()->getTextualGraphAt(type, format, address);
|
||||||
tempConfig.set("graph.gv.format", type);
|
if (!string) {
|
||||||
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";
|
|
||||||
return;
|
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)
|
bool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)
|
||||||
@ -403,40 +402,39 @@ bool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)
|
|||||||
|
|
||||||
Q_DECLARE_METATYPE(CutterGraphView::GraphExportType);
|
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 = {
|
QVector<MultitypeFileSaveDialog::TypeDescription> types = {
|
||||||
{ tr("PNG (*.png)"), "png", QVariant::fromValue(GraphExportType::Png) },
|
{ tr("PNG (*.png)"), "png", QVariant::fromValue(GraphExportType::Png) },
|
||||||
{ tr("JPEG (*.jpg)"), "jpg", QVariant::fromValue(GraphExportType::Jpeg) },
|
{ tr("JPEG (*.jpg)"), "jpg", QVariant::fromValue(GraphExportType::Jpeg) },
|
||||||
{ tr("SVG (*.svg)"), "svg", QVariant::fromValue(GraphExportType::Svg) }
|
{ tr("SVG (*.svg)"), "svg", QVariant::fromValue(GraphExportType::Svg) }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool rzGraphExports = !graphCommand.isEmpty();
|
types.append({
|
||||||
if (rzGraphExports) {
|
{ tr("Graphviz dot (*.dot)"), "dot", QVariant::fromValue(GraphExportType::GVDot) },
|
||||||
types.append({
|
{ tr("Graph Modelling Language (*.gml)"), "gml",
|
||||||
{ tr("Graphviz dot (*.dot)"), "dot", QVariant::fromValue(GraphExportType::GVDot) },
|
QVariant::fromValue(GraphExportType::RzGml) },
|
||||||
{ tr("Graph Modelling Language (*.gml)"), "gml",
|
{ tr("RZ JSON (*.json)"), "json", QVariant::fromValue(GraphExportType::RzJson) },
|
||||||
QVariant::fromValue(GraphExportType::RzGml) },
|
});
|
||||||
{ tr("RZ JSON (*.json)"), "json", QVariant::fromValue(GraphExportType::RzJson) },
|
|
||||||
{ tr("SDB key-value (*.txt)"), "txt",
|
bool hasGraphviz = !QStandardPaths::findExecutable("dot").isEmpty()
|
||||||
QVariant::fromValue(GraphExportType::RzSDBKeyValue) },
|
|| !QStandardPaths::findExecutable("xdot").isEmpty();
|
||||||
});
|
if (hasGraphviz) {
|
||||||
bool hasGraphviz = !QStandardPaths::findExecutable("dot").isEmpty()
|
types.append({ { tr("Graphviz json (*.json)"), "json",
|
||||||
|| !QStandardPaths::findExecutable("xdot").isEmpty();
|
QVariant::fromValue(GraphExportType::GVJson) },
|
||||||
if (hasGraphviz) {
|
{ tr("Graphviz gif (*.gif)"), "gif",
|
||||||
types.append({ { tr("Graphviz json (*.json)"), "json",
|
QVariant::fromValue(GraphExportType::GVGif) },
|
||||||
QVariant::fromValue(GraphExportType::GVJson) },
|
{ tr("Graphviz png (*.png)"), "png",
|
||||||
{ tr("Graphviz gif (*.gif)"), "gif",
|
QVariant::fromValue(GraphExportType::GVPng) },
|
||||||
QVariant::fromValue(GraphExportType::GVGif) },
|
{ tr("Graphviz jpg (*.jpg)"), "jpg",
|
||||||
{ tr("Graphviz png (*.png)"), "png",
|
QVariant::fromValue(GraphExportType::GVJpeg) },
|
||||||
QVariant::fromValue(GraphExportType::GVPng) },
|
{ tr("Graphviz PostScript (*.ps)"), "ps",
|
||||||
{ tr("Graphviz jpg (*.jpg)"), "jpg",
|
QVariant::fromValue(GraphExportType::GVPostScript) },
|
||||||
QVariant::fromValue(GraphExportType::GVJpeg) },
|
{ tr("Graphviz svg (*.svg)"), "svg",
|
||||||
{ tr("Graphviz PostScript (*.ps)"), "ps",
|
QVariant::fromValue(GraphExportType::GVSvg) },
|
||||||
QVariant::fromValue(GraphExportType::GVPostScript) },
|
{ tr("Graphviz pdf (*.pdf)"), "pdf",
|
||||||
{ tr("Graphviz svg (*.svg)"), "svg",
|
QVariant::fromValue(GraphExportType::GVPdf) } });
|
||||||
QVariant::fromValue(GraphExportType::GVSvg) } });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MultitypeFileSaveDialog dialog(this, tr("Export Graph"));
|
MultitypeFileSaveDialog dialog(this, tr("Export Graph"));
|
||||||
@ -470,5 +468,5 @@ void CutterGraphView::showExportGraphDialog(QString defaultName, QString graphCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString filePath = dialog.selectedFiles().first();
|
QString filePath = dialog.selectedFiles().first();
|
||||||
exportGraph(filePath, exportType, graphCommand, address);
|
exportGraph(filePath, exportType, type, address);
|
||||||
}
|
}
|
||||||
|
@ -32,48 +32,38 @@ public:
|
|||||||
GVJpeg,
|
GVJpeg,
|
||||||
GVPostScript,
|
GVPostScript,
|
||||||
GVSvg,
|
GVSvg,
|
||||||
|
GVPdf,
|
||||||
RzGml,
|
RzGml,
|
||||||
RzSDBKeyValue,
|
|
||||||
RzJson
|
RzJson
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* @brief Export graph to a file in the specified format
|
* @brief Export graph to a file in the specified format
|
||||||
* @param filePath
|
* @param filePath - output file path
|
||||||
* @param type export type, GV* and Rz* types require \p graphCommand
|
* @param exportType - export type, GV* and Rz* types require \p graphCommand
|
||||||
* @param graphCommand rizin graph printing command without type, not required for direct image
|
* @param graphType - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or
|
||||||
* export
|
* RZ_CORE_GRAPH_TYPE_IMPORT
|
||||||
* @param address object address for commands like agf
|
* @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);
|
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
|
* @brief Export graph in one of the text formats supported by rizin json, gml, SDB key-value
|
||||||
* @param filePath output file path
|
* @param filePath - output file path
|
||||||
* @param graphCommand graph command including the format, example "agfd" or "agfg"
|
* @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
|
||||||
* @param address object address if required by command
|
* @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);
|
static bool graphIsBitamp(GraphExportType type);
|
||||||
/**
|
/**
|
||||||
* @brief Show graph export dialog.
|
* @brief Show graph export dialog.
|
||||||
* @param defaultName - default file name in the export dialog
|
* @param defaultName - default file name in the export dialog
|
||||||
* @param graphCommand - rizin graph commmand with graph type and without export type, for
|
* @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT
|
||||||
* example afC. Leave empty for non-rizin graphs. In such case only direct image export will be
|
* @param address - object address (if global set it to RVA_INVALID)
|
||||||
* available.
|
|
||||||
* @param address - object address if relevant for \p graphCommand
|
|
||||||
*/
|
*/
|
||||||
void showExportGraphDialog(QString defaultName, QString graphCommand = "",
|
void showExportGraphDialog(QString defaultName, RzCoreGraphType type,
|
||||||
RVA address = RVA_INVALID);
|
RVA address = RVA_INVALID);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -4,8 +4,13 @@
|
|||||||
CutterTreeView::CutterTreeView(QWidget *parent) : QTreeView(parent), ui(new Ui::CutterTreeView())
|
CutterTreeView::CutterTreeView(QWidget *parent) : QTreeView(parent), ui(new Ui::CutterTreeView())
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
this->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
applyCutterStyle(this);
|
||||||
this->setUniformRowHeights(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CutterTreeView::~CutterTreeView() {}
|
CutterTreeView::~CutterTreeView() {}
|
||||||
|
|
||||||
|
void CutterTreeView::applyCutterStyle(QTreeView *view)
|
||||||
|
{
|
||||||
|
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
view->setUniformRowHeights(true);
|
||||||
|
}
|
||||||
|
@ -19,6 +19,8 @@ public:
|
|||||||
explicit CutterTreeView(QWidget *parent = nullptr);
|
explicit CutterTreeView(QWidget *parent = nullptr);
|
||||||
~CutterTreeView();
|
~CutterTreeView();
|
||||||
|
|
||||||
|
static void applyCutterStyle(QTreeView *view);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::CutterTreeView> ui;
|
std::unique_ptr<Ui::CutterTreeView> ui;
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include "ui_Dashboard.h"
|
#include "ui_Dashboard.h"
|
||||||
#include "common/Helpers.h"
|
#include "common/Helpers.h"
|
||||||
#include "common/JsonModel.h"
|
#include "common/JsonModel.h"
|
||||||
#include "common/JsonTreeItem.h"
|
|
||||||
#include "common/TempConfig.h"
|
#include "common/TempConfig.h"
|
||||||
#include "dialogs/VersionInfoDialog.h"
|
#include "dialogs/VersionInfoDialog.h"
|
||||||
|
|
||||||
@ -32,24 +31,27 @@ Dashboard::~Dashboard() {}
|
|||||||
|
|
||||||
void Dashboard::updateContents()
|
void Dashboard::updateContents()
|
||||||
{
|
{
|
||||||
CutterJson docu = Core()->getFileInfo();
|
RzCoreLocked core(Core());
|
||||||
CutterJson item = docu["core"];
|
int fd = rz_io_fd_get_current(core->io);
|
||||||
CutterJson item2 = docu["bin"];
|
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());
|
RzBinFile *bf = rz_bin_cur(core->bin);
|
||||||
setPlainText(this->ui->compilationDateEdit, item2["compiled"].toString());
|
if (bf) {
|
||||||
|
setPlainText(this->ui->compilationDateEdit, rz_core_bin_get_compile_time(bf));
|
||||||
if (!item2["relro"].toString().isEmpty()) {
|
if (bf->o) {
|
||||||
QString relro = item2["relro"].toString().section(QLatin1Char(' '), 0, 0);
|
char *relco_buf = sdb_get(bf->o->kv, "elf.relro", 0);
|
||||||
relro[0] = relro[0].toUpper();
|
if (RZ_STR_ISNOTEMPTY(relco_buf)) {
|
||||||
setPlainText(this->ui->relroEdit, relro);
|
QString relro = QString(relco_buf).section(QLatin1Char(' '), 0, 0);
|
||||||
} else {
|
relro[0] = relro[0].toUpper();
|
||||||
setPlainText(this->ui->relroEdit, "N/A");
|
setPlainText(this->ui->relroEdit, relro);
|
||||||
|
} else {
|
||||||
|
setPlainText(this->ui->relroEdit, "N/A");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add file hashes, analysis info and libraries
|
// 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);
|
RzBinInfo *binInfo = rz_bin_get_info(core->bin);
|
||||||
|
|
||||||
setPlainText(ui->fileEdit, binInfo ? binInfo->file : "");
|
setPlainText(ui->fileEdit, binInfo ? binInfo->file : "");
|
||||||
@ -111,82 +113,68 @@ void Dashboard::updateContents()
|
|||||||
hashesLayout->addRow(new QLabel(label), hashLineEdit);
|
hashesLayout->addRow(new QLabel(label), hashLineEdit);
|
||||||
}
|
}
|
||||||
|
|
||||||
CutterJson analinfo = Core()->cmdj("aaij");
|
st64 fcns = rz_list_length(core->analysis->fcns);
|
||||||
setPlainText(ui->functionsLineEdit, QString::number(analinfo["fcns"].toSt64()));
|
st64 strs = rz_flag_count(core->flags, "str.*");
|
||||||
setPlainText(ui->xRefsLineEdit, QString::number(analinfo["xrefs"].toSt64()));
|
st64 syms = rz_flag_count(core->flags, "sym.*");
|
||||||
setPlainText(ui->callsLineEdit, QString::number(analinfo["calls"].toSt64()));
|
st64 imps = rz_flag_count(core->flags, "sym.imp.*");
|
||||||
setPlainText(ui->stringsLineEdit, QString::number(analinfo["strings"].toSt64()));
|
st64 code = rz_core_analysis_code_count(core);
|
||||||
setPlainText(ui->symbolsLineEdit, QString::number(analinfo["symbols"].toSt64()));
|
st64 covr = rz_core_analysis_coverage_count(core);
|
||||||
setPlainText(ui->importsLineEdit, QString::number(analinfo["imports"].toSt64()));
|
st64 call = rz_core_analysis_calls_count(core);
|
||||||
setPlainText(ui->coverageLineEdit, QString::number(analinfo["covrage"].toSt64()) + " bytes");
|
ut64 xrfs = rz_analysis_xrefs_count(core->analysis);
|
||||||
setPlainText(ui->codeSizeLineEdit, QString::number(analinfo["codesz"].toSt64()) + " bytes");
|
double precentage = (code > 0) ? (covr * 100.0 / code) : 0;
|
||||||
setPlainText(ui->percentageLineEdit, QString::number(analinfo["percent"].toSt64()) + "%");
|
|
||||||
|
|
||||||
// dunno: why not label->setText(lines.join("\n")?
|
setPlainText(ui->functionsLineEdit, QString::number(fcns));
|
||||||
while (ui->verticalLayout_2->count() > 0) {
|
setPlainText(ui->xRefsLineEdit, QString::number(xrfs));
|
||||||
QLayoutItem *item = ui->verticalLayout_2->takeAt(0);
|
setPlainText(ui->callsLineEdit, QString::number(call));
|
||||||
if (item != nullptr) {
|
setPlainText(ui->stringsLineEdit, QString::number(strs));
|
||||||
QWidget *w = item->widget();
|
setPlainText(ui->symbolsLineEdit, QString::number(syms));
|
||||||
if (w != nullptr) {
|
setPlainText(ui->importsLineEdit, QString::number(imps));
|
||||||
w->deleteLater();
|
setPlainText(ui->coverageLineEdit, QString::number(covr) + " bytes");
|
||||||
}
|
setPlainText(ui->codeSizeLineEdit, QString::number(code) + " bytes");
|
||||||
|
setPlainText(ui->percentageLineEdit, QString::number(precentage) + "%");
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ui->libraryList->setPlainText("");
|
||||||
const RzList *libs = bf ? rz_bin_object_get_libs(bf->o) : nullptr;
|
const RzList *libs = bf ? rz_bin_object_get_libs(bf->o) : nullptr;
|
||||||
if (libs) {
|
if (libs) {
|
||||||
|
QString libText;
|
||||||
|
bool first = true;
|
||||||
for (const auto &lib : CutterRzList<char>(libs)) {
|
for (const auto &lib : CutterRzList<char>(libs)) {
|
||||||
auto *label = new QLabel(this);
|
if (!first) {
|
||||||
label->setText(lib);
|
libText.append("\n");
|
||||||
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
}
|
||||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
libText.append(lib);
|
||||||
ui->verticalLayout_2->addWidget(label);
|
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
|
// Check if signature info and version info available
|
||||||
if (Core()->getSignatureInfo().isEmpty()) {
|
if (!Core()->getSignatureInfo().size()) {
|
||||||
ui->certificateButton->setEnabled(false);
|
ui->certificateButton->setEnabled(false);
|
||||||
}
|
}
|
||||||
if (!Core()->getFileVersionInfo().size()) {
|
ui->versioninfoButton->setEnabled(Core()->existsFileInfo());
|
||||||
ui->versioninfoButton->setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dashboard::on_certificateButton_clicked()
|
void Dashboard::on_certificateButton_clicked()
|
||||||
{
|
{
|
||||||
static QDialog *viewDialog = nullptr;
|
QDialog dialog(this);
|
||||||
static CutterTreeView *view = nullptr;
|
auto view = new QTreeWidget(&dialog);
|
||||||
static JsonModel *model = nullptr;
|
view->setHeaderLabels({ tr("Key"), tr("Value") });
|
||||||
static QString qstrCertificates;
|
view->addTopLevelItem(Cutter::jsonTreeWidgetItem(QString("<%1>").arg(tr("root")),
|
||||||
if (!viewDialog) {
|
Core()->getSignatureInfo()));
|
||||||
viewDialog = new QDialog(this);
|
CutterTreeView::applyCutterStyle(view);
|
||||||
view = new CutterTreeView(viewDialog);
|
view->expandAll();
|
||||||
model = new JsonModel();
|
view->resize(900, 600);
|
||||||
qstrCertificates = Core()->getSignatureInfo();
|
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
}
|
sizePolicy.setHorizontalStretch(0);
|
||||||
if (!viewDialog->isVisible()) {
|
sizePolicy.setVerticalStretch(0);
|
||||||
std::string strCertificates = qstrCertificates.toUtf8().constData();
|
sizePolicy.setHeightForWidth(view->sizePolicy().hasHeightForWidth());
|
||||||
model->loadJson(QByteArray::fromStdString(strCertificates));
|
dialog.setSizePolicy(sizePolicy);
|
||||||
view->setModel(model);
|
dialog.setMinimumSize(QSize(900, 600));
|
||||||
view->expandAll();
|
dialog.setMaximumSize(QSize(900, 600));
|
||||||
view->resize(900, 600);
|
dialog.setSizeGripEnabled(false);
|
||||||
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
dialog.setWindowTitle("Certificates");
|
||||||
sizePolicy.setHorizontalStretch(0);
|
dialog.exec();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dashboard::on_versioninfoButton_clicked()
|
void Dashboard::on_versioninfoButton_clicked()
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
<enum>QFrame::Plain</enum>
|
<enum>QFrame::Plain</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalScrollBarPolicy">
|
<property name="verticalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="horizontalScrollBarPolicy">
|
<property name="horizontalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
@ -1331,10 +1331,33 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</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>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<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>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
@ -109,14 +109,14 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
|||||||
if (c.isValid()) {
|
if (c.isValid()) {
|
||||||
bbh->highlight(currBlockEntry, c);
|
bbh->highlight(currBlockEntry, c);
|
||||||
}
|
}
|
||||||
Config()->colorsUpdated();
|
emit Config()->colorsUpdated();
|
||||||
});
|
});
|
||||||
|
|
||||||
actionUnhighlight.setText(tr("Unhighlight block"));
|
actionUnhighlight.setText(tr("Unhighlight block"));
|
||||||
connect(&actionUnhighlight, &QAction::triggered, this, [this]() {
|
connect(&actionUnhighlight, &QAction::triggered, this, [this]() {
|
||||||
auto bbh = Core()->getBBHighlighter();
|
auto bbh = Core()->getBBHighlighter();
|
||||||
bbh->clear(blockForAddress(this->seekable->getOffset())->entry);
|
bbh->clear(blockForAddress(this->seekable->getOffset())->entry);
|
||||||
Config()->colorsUpdated();
|
emit Config()->colorsUpdated();
|
||||||
});
|
});
|
||||||
|
|
||||||
QAction *highlightBI = new QAction(this);
|
QAction *highlightBI = new QAction(this);
|
||||||
@ -162,15 +162,15 @@ void DisassemblerGraphView::connectSeekChanged(bool disconn)
|
|||||||
|
|
||||||
DisassemblerGraphView::~DisassemblerGraphView()
|
DisassemblerGraphView::~DisassemblerGraphView()
|
||||||
{
|
{
|
||||||
for (QShortcut *shortcut : shortcuts) {
|
qDeleteAll(shortcuts);
|
||||||
delete shortcut;
|
shortcuts.clear();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblerGraphView::refreshView()
|
void DisassemblerGraphView::refreshView()
|
||||||
{
|
{
|
||||||
CutterGraphView::refreshView();
|
CutterGraphView::refreshView();
|
||||||
loadCurrentGraph();
|
loadCurrentGraph();
|
||||||
|
breakpoints = Core()->getBreakpointsAddresses();
|
||||||
emit viewRefreshed();
|
emit viewRefreshed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,13 +182,6 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||||||
.set("asm.lines", false)
|
.set("asm.lines", false)
|
||||||
.set("asm.lines.fcn", 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();
|
disassembly_blocks.clear();
|
||||||
blocks.clear();
|
blocks.clear();
|
||||||
|
|
||||||
@ -197,7 +190,19 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||||||
highlight_token = nullptr;
|
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 (emptyGraph) {
|
||||||
// If there's no function to print, just add a message
|
// If there's no function to print, just add a message
|
||||||
if (!emptyText) {
|
if (!emptyText) {
|
||||||
@ -213,31 +218,20 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||||||
}
|
}
|
||||||
// Refresh global "empty graph" variable so other widget know there is nothing to show here
|
// Refresh global "empty graph" variable so other widget know there is nothing to show here
|
||||||
Core()->setGraphEmpty(emptyGraph);
|
Core()->setGraphEmpty(emptyGraph);
|
||||||
|
setEntry(fcn ? fcn->addr : RVA_INVALID);
|
||||||
|
|
||||||
CutterJson func = functions.first();
|
if (!fcn) {
|
||||||
|
return;
|
||||||
windowTitle = tr("Graph");
|
|
||||||
QString funcName = func["name"].toString().trimmed();
|
|
||||||
if (emptyGraph) {
|
|
||||||
windowTitle += " (Empty)";
|
|
||||||
} else if (!funcName.isEmpty()) {
|
|
||||||
windowTitle += " (" + funcName + ")";
|
|
||||||
}
|
}
|
||||||
emit nameChanged(windowTitle);
|
|
||||||
|
|
||||||
RVA entry = func["offset"].toRVA();
|
for (const auto &bbi : CutterRzList<RzAnalysisBlock>(fcn->bbs)) {
|
||||||
|
RVA bbiFail = bbi->fail;
|
||||||
setEntry(entry);
|
RVA bbiJump = bbi->jump;
|
||||||
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();
|
|
||||||
|
|
||||||
DisassemblyBlock db;
|
DisassemblyBlock db;
|
||||||
GraphBlock gb;
|
GraphBlock gb;
|
||||||
gb.entry = block_entry;
|
gb.entry = bbi->addr;
|
||||||
db.entry = block_entry;
|
db.entry = bbi->addr;
|
||||||
if (Config()->getGraphBlockEntryOffset()) {
|
if (Config()->getGraphBlockEntryOffset()) {
|
||||||
// QColor(0,0,0,0) is transparent
|
// QColor(0,0,0,0) is transparent
|
||||||
db.header_text = Text("[" + RzAddressString(db.entry) + "]", ConfigColor("offset"),
|
db.header_text = Text("[" + RzAddressString(db.entry) + "]", ConfigColor("offset"),
|
||||||
@ -245,50 +239,67 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||||||
}
|
}
|
||||||
db.true_path = RVA_INVALID;
|
db.true_path = RVA_INVALID;
|
||||||
db.false_path = RVA_INVALID;
|
db.false_path = RVA_INVALID;
|
||||||
if (block_fail) {
|
if (bbiFail) {
|
||||||
db.false_path = block_fail;
|
db.false_path = bbiFail;
|
||||||
gb.edges.emplace_back(block_fail);
|
gb.edges.emplace_back(bbiFail);
|
||||||
}
|
}
|
||||||
if (block_jump) {
|
if (bbiJump) {
|
||||||
if (block_fail) {
|
if (bbiFail) {
|
||||||
db.true_path = block_jump;
|
db.true_path = bbiJump;
|
||||||
}
|
}
|
||||||
gb.edges.emplace_back(block_jump);
|
gb.edges.emplace_back(bbiJump);
|
||||||
}
|
}
|
||||||
|
|
||||||
CutterJson switchOp = block["switchop"];
|
RzAnalysisSwitchOp *switchOp = bbi->switch_op;
|
||||||
if (switchOp.size()) {
|
if (switchOp) {
|
||||||
for (CutterJson caseOp : switchOp["cases"]) {
|
for (const auto &caseOp : CutterRzList<RzAnalysisCaseOp>(switchOp->cases)) {
|
||||||
RVA caseJump = caseOp["jump"].toRVA();
|
if (caseOp->jump == RVA_INVALID) {
|
||||||
if (caseJump == RVA_INVALID) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
gb.edges.emplace_back(caseJump);
|
gb.edges.emplace_back(caseOp->jump);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CutterJson opArray = block["ops"];
|
RzCoreLocked core(Core());
|
||||||
CutterJson::iterator iterator = opArray.begin();
|
std::unique_ptr<ut8[]> buf { new ut8[bbi->size] };
|
||||||
while (iterator != opArray.end()) {
|
if (!buf) {
|
||||||
CutterJson op = *iterator;
|
break;
|
||||||
Instr i;
|
}
|
||||||
i.addr = op["offset"].toUt64();
|
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 ...
|
// get instruction size from distance to next instruction ...
|
||||||
RVA nextOffset = (*iterator)["offset"].toRVA();
|
RVA nextOffset = (*iter)->offset;
|
||||||
i.size = nextOffset - i.addr;
|
instr.size = nextOffset - instr.addr;
|
||||||
} else {
|
} else {
|
||||||
// or to the end of the block.
|
// 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;
|
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);
|
RichTextPainter::List richText = RichTextPainter::fromTextDocument(textDoc);
|
||||||
// Colors::colorizeAssembly(richText, textDoc.toPlainText(), 0);
|
// Colors::colorizeAssembly(richText, textDoc.toPlainText(), 0);
|
||||||
@ -296,23 +307,19 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||||||
bool cropped;
|
bool cropped;
|
||||||
int blockLength = Config()->getGraphBlockMaxChars()
|
int blockLength = Config()->getGraphBlockMaxChars()
|
||||||
+ Core()->getConfigb("asm.bytes") * 24 + Core()->getConfigb("asm.emu") * 10;
|
+ 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)
|
if (cropped)
|
||||||
i.fullText = richText;
|
instr.fullText = richText;
|
||||||
else
|
else
|
||||||
i.fullText = Text();
|
instr.fullText = Text();
|
||||||
db.instrs.push_back(i);
|
db.instrs.push_back(instr);
|
||||||
}
|
}
|
||||||
disassembly_blocks[db.entry] = db;
|
disassembly_blocks[db.entry] = db;
|
||||||
prepareGraphNode(gb);
|
prepareGraphNode(gb);
|
||||||
|
|
||||||
addBlock(gb);
|
addBlock(gb);
|
||||||
}
|
}
|
||||||
cleanupEdges(blocks);
|
cleanupEdges(blocks);
|
||||||
|
computeGraphPlacement();
|
||||||
if (func["blocks"].size()) {
|
|
||||||
computeGraphPlacement();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DisassemblerGraphView::EdgeConfigurationMapping DisassemblerGraphView::getEdgeConfigurations()
|
DisassemblerGraphView::EdgeConfigurationMapping DisassemblerGraphView::getEdgeConfigurations()
|
||||||
@ -365,8 +372,6 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block,
|
|||||||
p.setFont(Config()->getFont());
|
p.setFont(Config()->getFont());
|
||||||
p.drawRect(blockRect);
|
p.drawRect(blockRect);
|
||||||
|
|
||||||
breakpoints = Core()->getBreakpointsAddresses();
|
|
||||||
|
|
||||||
// Render node
|
// Render node
|
||||||
DisassemblyBlock &db = disassembly_blocks[block.entry];
|
DisassemblyBlock &db = disassembly_blocks[block.entry];
|
||||||
bool block_selected = false;
|
bool block_selected = false;
|
||||||
@ -887,6 +892,11 @@ void DisassemblerGraphView::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
|
|
||||||
void DisassemblerGraphView::showExportDialog()
|
void DisassemblerGraphView::showExportDialog()
|
||||||
{
|
{
|
||||||
|
if (currentFcnAddr == RVA_INVALID) {
|
||||||
|
qWarning() << "Cannot find current function.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QString defaultName = "graph";
|
QString defaultName = "graph";
|
||||||
if (auto f = Core()->functionIn(currentFcnAddr)) {
|
if (auto f = Core()->functionIn(currentFcnAddr)) {
|
||||||
QString functionName = f->name;
|
QString functionName = f->name;
|
||||||
@ -897,7 +907,7 @@ void DisassemblerGraphView::showExportDialog()
|
|||||||
defaultName = functionName;
|
defaultName = functionName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showExportGraphDialog(defaultName, "agf", currentFcnAddr);
|
showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_BLOCK_FUN, currentFcnAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event,
|
void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event,
|
||||||
|
@ -211,7 +211,7 @@ QString DisassemblyWidget::getWidgetType()
|
|||||||
|
|
||||||
QFontMetrics DisassemblyWidget::getFontMetrics()
|
QFontMetrics DisassemblyWidget::getFontMetrics()
|
||||||
{
|
{
|
||||||
return mDisasTextEdit->fontMetrics();
|
return QFontMetrics(mDisasTextEdit->font());
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<DisassemblyLine> DisassemblyWidget::getLines()
|
QList<DisassemblyLine> DisassemblyWidget::getLines()
|
||||||
|
@ -231,7 +231,15 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
|
|||||||
|
|
||||||
QStringList disasmPreview =
|
QStringList disasmPreview =
|
||||||
Core()->getDisassemblyPreview(function.offset, kMaxTooltipDisasmPreviewLines);
|
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();
|
const QFont &fnt = Config()->getFont();
|
||||||
QFontMetrics fm { fnt };
|
QFontMetrics fm { fnt };
|
||||||
|
|
||||||
@ -245,7 +253,7 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (disasmPreview.isEmpty() && highlights.isEmpty())
|
if (disasmPreview.isEmpty() && highlights.isEmpty())
|
||||||
return QVariant();
|
return {};
|
||||||
|
|
||||||
QString toolTipContent =
|
QString toolTipContent =
|
||||||
QString("<html><div style=\"font-family: %1; font-size: %2pt; white-space: "
|
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);
|
return importAddresses->contains(function.offset);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,7 +572,18 @@ void FunctionsWidget::refreshTree()
|
|||||||
importAddresses.insert(import.plt);
|
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->updateCurrentIndex();
|
||||||
functionModel->endResetModel();
|
functionModel->endResetModel();
|
||||||
|
@ -703,7 +703,10 @@ void GraphView::mouseDoubleClickEvent(QMouseEvent *event)
|
|||||||
|
|
||||||
void GraphView::keyPressEvent(QKeyEvent *event)
|
void GraphView::keyPressEvent(QKeyEvent *event)
|
||||||
{
|
{
|
||||||
|
// for scrolling with arrow keys
|
||||||
const int delta = static_cast<int>(30.0 / current_scale);
|
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;
|
int dx = 0, dy = 0;
|
||||||
switch (event->key()) {
|
switch (event->key()) {
|
||||||
case Qt::Key_Up:
|
case Qt::Key_Up:
|
||||||
@ -718,6 +721,12 @@ void GraphView::keyPressEvent(QKeyEvent *event)
|
|||||||
case Qt::Key_Right:
|
case Qt::Key_Right:
|
||||||
dx = delta;
|
dx = delta;
|
||||||
break;
|
break;
|
||||||
|
case Qt::Key_PageUp:
|
||||||
|
dy = -delta2;
|
||||||
|
break;
|
||||||
|
case Qt::Key_PageDown:
|
||||||
|
dy = delta2;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
QAbstractScrollArea::keyPressEvent(event);
|
QAbstractScrollArea::keyPressEvent(event);
|
||||||
return;
|
return;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ struct BasicCursor
|
|||||||
{
|
{
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
bool pastEnd;
|
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() : address(0), pastEnd(false) {}
|
||||||
BasicCursor &operator+=(int64_t offset)
|
BasicCursor &operator+=(int64_t offset)
|
||||||
{
|
{
|
||||||
@ -35,6 +35,14 @@ struct BasicCursor
|
|||||||
*this += int64_t(offset);
|
*this += int64_t(offset);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool moveChecked(int offset)
|
||||||
|
{
|
||||||
|
auto oldAddress = address;
|
||||||
|
*this += offset;
|
||||||
|
return address - oldAddress == uint64_t(offset);
|
||||||
|
}
|
||||||
|
|
||||||
BasicCursor &operator+=(uint64_t offset)
|
BasicCursor &operator+=(uint64_t offset)
|
||||||
{
|
{
|
||||||
if (uint64_t(offset) > (UINT64_MAX - address)) {
|
if (uint64_t(offset) > (UINT64_MAX - address)) {
|
||||||
@ -46,7 +54,10 @@ struct BasicCursor
|
|||||||
}
|
}
|
||||||
return *this;
|
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
|
struct HexCursor
|
||||||
@ -74,9 +85,10 @@ struct HexCursor
|
|||||||
class AbstractData
|
class AbstractData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~AbstractData() {}
|
virtual ~AbstractData() = default;
|
||||||
virtual void fetch(uint64_t addr, int len) = 0;
|
virtual void fetch(uint64_t addr, int len) = 0;
|
||||||
virtual bool copy(void *out, uint64_t adr, size_t 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 maxIndex() = 0;
|
||||||
virtual uint64_t minIndex() = 0;
|
virtual uint64_t minIndex() = 0;
|
||||||
};
|
};
|
||||||
@ -86,7 +98,7 @@ class BufferData : public AbstractData
|
|||||||
public:
|
public:
|
||||||
BufferData() { m_buffer.fill(0, 1); }
|
BufferData() { m_buffer.fill(0, 1); }
|
||||||
|
|
||||||
BufferData(const QByteArray &buffer)
|
explicit BufferData(const QByteArray &buffer)
|
||||||
{
|
{
|
||||||
if (buffer.isEmpty()) {
|
if (buffer.isEmpty()) {
|
||||||
m_buffer.fill(0, 1);
|
m_buffer.fill(0, 1);
|
||||||
@ -95,7 +107,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~BufferData() override {}
|
~BufferData() override = default;
|
||||||
|
|
||||||
void fetch(uint64_t, int) override {}
|
void fetch(uint64_t, int) override {}
|
||||||
|
|
||||||
@ -109,6 +121,16 @@ public:
|
|||||||
return false;
|
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; }
|
uint64_t maxIndex() override { return m_buffer.size() - 1; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -118,8 +140,8 @@ private:
|
|||||||
class MemoryData : public AbstractData
|
class MemoryData : public AbstractData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MemoryData() {}
|
MemoryData() = default;
|
||||||
~MemoryData() override {}
|
~MemoryData() override = default;
|
||||||
static constexpr size_t BLOCK_SIZE = 4096;
|
static constexpr size_t BLOCK_SIZE = 4096;
|
||||||
|
|
||||||
void fetch(uint64_t address, int length) override
|
void fetch(uint64_t address, int length) override
|
||||||
@ -144,10 +166,11 @@ public:
|
|||||||
|
|
||||||
bool copy(void *out, uint64_t addr, size_t len) override
|
bool copy(void *out, uint64_t addr, size_t len) override
|
||||||
{
|
{
|
||||||
if (addr < m_firstBlockAddr || addr > m_lastValidAddr
|
if (addr < m_firstBlockAddr
|
||||||
|| (m_lastValidAddr - addr + 1)
|
|| addr > m_lastValidAddr
|
||||||
< len /* do not merge with last check to handle overflows */
|
/* do not merge with previous check to handle overflows */
|
||||||
|| m_blocks.isEmpty()) {
|
|| (m_lastValidAddr - addr + 1) < len || m_blocks.isEmpty()) {
|
||||||
|
memset(out, 0xff, len);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,9 +188,47 @@ public:
|
|||||||
return true;
|
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:
|
private:
|
||||||
QVector<QByteArray> m_blocks;
|
QVector<QByteArray> m_blocks;
|
||||||
@ -178,7 +239,11 @@ private:
|
|||||||
class HexSelection
|
class HexSelection
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HexSelection() { m_empty = true; }
|
HexSelection()
|
||||||
|
{
|
||||||
|
m_empty = true;
|
||||||
|
m_start = m_end = 0;
|
||||||
|
}
|
||||||
|
|
||||||
inline void init(BasicCursor addr)
|
inline void init(BasicCursor addr)
|
||||||
{
|
{
|
||||||
@ -189,7 +254,8 @@ public:
|
|||||||
void set(uint64_t start, uint64_t end)
|
void set(uint64_t start, uint64_t end)
|
||||||
{
|
{
|
||||||
m_empty = false;
|
m_empty = false;
|
||||||
m_init = m_start = start;
|
m_init = BasicCursor(start);
|
||||||
|
m_start = start;
|
||||||
m_end = end;
|
m_end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +285,7 @@ public:
|
|||||||
|
|
||||||
bool contains(uint64_t pos) const { return !m_empty && m_start <= pos && pos <= m_end; }
|
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;
|
uint64_t size = 0;
|
||||||
if (!isEmpty())
|
if (!isEmpty())
|
||||||
@ -227,9 +293,9 @@ public:
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isEmpty() { return m_empty; }
|
inline bool isEmpty() const { return m_empty; }
|
||||||
inline uint64_t start() { return m_start; }
|
inline uint64_t start() const { return m_start; }
|
||||||
inline uint64_t end() { return m_end; }
|
inline uint64_t end() const { return m_end; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BasicCursor m_init;
|
BasicCursor m_init;
|
||||||
@ -244,7 +310,7 @@ class HexWidget : public QScrollArea
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit HexWidget(QWidget *parent = nullptr);
|
explicit HexWidget(QWidget *parent = nullptr);
|
||||||
~HexWidget();
|
~HexWidget() override = default;
|
||||||
|
|
||||||
void setMonospaceFont(const QFont &font);
|
void setMonospaceFont(const QFont &font);
|
||||||
|
|
||||||
@ -258,10 +324,12 @@ public:
|
|||||||
ItemFormatFloat
|
ItemFormatFloat
|
||||||
};
|
};
|
||||||
enum class ColumnMode { Fixed, PowerOf2 };
|
enum class ColumnMode { Fixed, PowerOf2 };
|
||||||
|
enum class EditWordState { Read, WriteNotStarted, WriteNotEdited, WriteEdited };
|
||||||
|
enum class HexNavigationMode { Words, WordChar, AnyChar };
|
||||||
|
|
||||||
void setItemSize(int nbytes);
|
void setItemSize(int nbytes);
|
||||||
void setItemFormat(ItemFormat format);
|
void setItemFormat(ItemFormat format);
|
||||||
void setItemEndianess(bool bigEndian);
|
void setItemEndianness(bool bigEndian);
|
||||||
void setItemGroupSize(int size);
|
void setItemGroupSize(int size);
|
||||||
/**
|
/**
|
||||||
* @brief Sets line size in bytes.
|
* @brief Sets line size in bytes.
|
||||||
@ -292,7 +360,7 @@ public slots:
|
|||||||
void refresh();
|
void refresh();
|
||||||
void updateColors();
|
void updateColors();
|
||||||
signals:
|
signals:
|
||||||
void selectionChanged(Selection selection);
|
void selectionChanged(HexWidget::Selection selection);
|
||||||
void positionChanged(RVA start);
|
void positionChanged(RVA start);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -300,10 +368,12 @@ protected:
|
|||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
void wheelEvent(QWheelEvent *event) override;
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
|
bool event(QEvent *event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onCursorBlinked();
|
void onCursorBlinked();
|
||||||
@ -311,13 +381,13 @@ private slots:
|
|||||||
void copy();
|
void copy();
|
||||||
void copyAddress();
|
void copyAddress();
|
||||||
void onRangeDialogAccepted();
|
void onRangeDialogAccepted();
|
||||||
void on_actionAddComment_triggered();
|
void onActionAddCommentTriggered();
|
||||||
void on_actionDeleteComment_triggered();
|
void onActionDeleteCommentTriggered();
|
||||||
|
|
||||||
// Write command slots
|
// Write command slots
|
||||||
void w_writeString();
|
void w_writeString();
|
||||||
void w_increaseDecrease();
|
void w_increaseDecrease();
|
||||||
void w_writeBytes();
|
void w_writeBytes();
|
||||||
void w_writeZeros();
|
void w_writeZeros();
|
||||||
void w_write64();
|
void w_write64();
|
||||||
void w_writeRandom();
|
void w_writeRandom();
|
||||||
@ -326,6 +396,9 @@ private slots:
|
|||||||
void w_writeWideString();
|
void w_writeWideString();
|
||||||
void w_writeCString();
|
void w_writeCString();
|
||||||
|
|
||||||
|
void onKeyboardEditTriggered(bool enabled);
|
||||||
|
void onKeyboardEditChanged(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateItemLength();
|
void updateItemLength();
|
||||||
void updateCounts();
|
void updateCounts();
|
||||||
@ -338,12 +411,15 @@ private:
|
|||||||
void updateMetrics();
|
void updateMetrics();
|
||||||
void updateAreasPosition();
|
void updateAreasPosition();
|
||||||
void updateAreasHeight();
|
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 setCursorAddr(BasicCursor addr, bool select = false);
|
||||||
void updateCursorMeta();
|
void updateCursorMeta();
|
||||||
void setCursorOnAscii(bool ascii);
|
void setCursorOnAscii(bool ascii);
|
||||||
bool isItemDifferentAt(uint64_t address);
|
bool isItemDifferentAt(uint64_t address);
|
||||||
const QColor itemColor(uint8_t byte);
|
QColor itemColor(uint8_t byte);
|
||||||
QVariant readItem(int offset, QColor *color = nullptr);
|
QVariant readItem(int offset, QColor *color = nullptr);
|
||||||
QString renderItem(int offset, QColor *color = nullptr);
|
QString renderItem(int offset, QColor *color = nullptr);
|
||||||
QChar renderAscii(int offset, QColor *color = nullptr);
|
QChar renderAscii(int offset, QColor *color = nullptr);
|
||||||
@ -359,12 +435,13 @@ private:
|
|||||||
/**
|
/**
|
||||||
* @brief Convert mouse position to address.
|
* @brief Convert mouse position to address.
|
||||||
* @param point mouse position in widget
|
* @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
|
* position between symbols, middle=false for insert mode cursor and getting symbol under
|
||||||
* cursor.
|
* cursor.
|
||||||
* @return
|
* @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 asciiPosToAddr(const QPoint &point, bool middle = false) const;
|
||||||
BasicCursor currentAreaPosToAddr(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;
|
BasicCursor mousePosToAddr(const QPoint &point, bool middle = false) const;
|
||||||
@ -412,6 +489,27 @@ private:
|
|||||||
inline uint64_t lastVisibleAddr() const { return (startAddress - 1) + bytesPerScreen(); }
|
inline uint64_t lastVisibleAddr() const { return (startAddress - 1) + bytesPerScreen(); }
|
||||||
|
|
||||||
const QRectF ¤tArea() const { return cursorOnAscii ? asciiArea : itemArea; }
|
const QRectF ¤tArea() 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 cursorEnabled;
|
||||||
bool cursorOnAscii;
|
bool cursorOnAscii;
|
||||||
@ -436,14 +534,13 @@ private:
|
|||||||
ItemFormat itemFormat;
|
ItemFormat itemFormat;
|
||||||
|
|
||||||
bool itemBigEndian;
|
bool itemBigEndian;
|
||||||
|
QString itemPrefix;
|
||||||
|
|
||||||
int visibleLines;
|
int visibleLines;
|
||||||
uint64_t startAddress;
|
uint64_t startAddress;
|
||||||
qreal charWidth;
|
qreal charWidth;
|
||||||
int byteWidth;
|
|
||||||
qreal lineHeight;
|
qreal lineHeight;
|
||||||
int addrCharLen;
|
int addrCharLen;
|
||||||
int addrAreaWidth;
|
|
||||||
QFont monospaceFont;
|
QFont monospaceFont;
|
||||||
|
|
||||||
bool showHeader;
|
bool showHeader;
|
||||||
@ -460,6 +557,7 @@ private:
|
|||||||
QColor b0x7fColor;
|
QColor b0x7fColor;
|
||||||
QColor b0xffColor;
|
QColor b0xffColor;
|
||||||
QColor printableColor;
|
QColor printableColor;
|
||||||
|
QColor warningColor;
|
||||||
|
|
||||||
HexdumpRangeDialog rangeDialog;
|
HexdumpRangeDialog rangeDialog;
|
||||||
|
|
||||||
@ -479,14 +577,30 @@ private:
|
|||||||
QAction *actionCopyAddress;
|
QAction *actionCopyAddress;
|
||||||
QAction *actionComment;
|
QAction *actionComment;
|
||||||
QAction *actionDeleteComment;
|
QAction *actionDeleteComment;
|
||||||
QAction *actionSetFlag;
|
|
||||||
QAction *actionSelectRange;
|
QAction *actionSelectRange;
|
||||||
|
QAction *actionKeyboardEdit;
|
||||||
QList<QAction *> actionsWriteString;
|
QList<QAction *> actionsWriteString;
|
||||||
QList<QAction *> actionsWriteOther;
|
QList<QAction *> actionsWriteOther;
|
||||||
|
|
||||||
std::unique_ptr<AbstractData> oldData;
|
std::unique_ptr<AbstractData> oldData;
|
||||||
std::unique_ptr<AbstractData> data;
|
std::unique_ptr<AbstractData> data;
|
||||||
IOModesController ioModesController;
|
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
|
#endif // HEXWIDGET_H
|
||||||
|
@ -144,7 +144,6 @@ void HexdumpWidget::initParsing()
|
|||||||
ui->parseTypeComboBox->addItem(tr("String"), "pcs");
|
ui->parseTypeComboBox->addItem(tr("String"), "pcs");
|
||||||
ui->parseTypeComboBox->addItem(tr("Assembler"), "pca");
|
ui->parseTypeComboBox->addItem(tr("Assembler"), "pca");
|
||||||
ui->parseTypeComboBox->addItem(tr("C bytes"), "pc");
|
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 half-words (2 byte)"), "pch");
|
||||||
ui->parseTypeComboBox->addItem(tr("C words (4 byte)"), "pcw");
|
ui->parseTypeComboBox->addItem(tr("C words (4 byte)"), "pcw");
|
||||||
ui->parseTypeComboBox->addItem(tr("C dwords (8 byte)"), "pcd");
|
ui->parseTypeComboBox->addItem(tr("C dwords (8 byte)"), "pcd");
|
||||||
@ -240,28 +239,28 @@ void HexdumpWidget::updateParseWindow(RVA start_address, int size)
|
|||||||
|
|
||||||
ui->hexDisasTextEdit->setPlainText(
|
ui->hexDisasTextEdit->setPlainText(
|
||||||
selectedCommand != "" ? Core()->cmdRawAt(
|
selectedCommand != "" ? Core()->cmdRawAt(
|
||||||
QString("%1 %2").arg(selectedCommand).arg(size), start_address)
|
QString("%1 @! %2").arg(selectedCommand).arg(size), start_address)
|
||||||
: "");
|
: "");
|
||||||
} else {
|
} else {
|
||||||
// Fill the information tab hashes and entropy
|
// Fill the information tab hashes and entropy
|
||||||
RzMsgDigestSize digest_size = 0;
|
RzHashSize digest_size = 0;
|
||||||
RzCoreLocked core(Core());
|
RzCoreLocked core(Core());
|
||||||
ut64 old_offset = core->offset;
|
ut64 old_offset = core->offset;
|
||||||
rz_core_seek(core, start_address, true);
|
rz_core_seek(core, start_address, true);
|
||||||
ut8 *block = core->block;
|
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));
|
ui->bytesMD5->setText(QString(digest));
|
||||||
free(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));
|
ui->bytesSHA1->setText(QString(digest));
|
||||||
free(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));
|
ui->bytesSHA256->setText(QString(digest));
|
||||||
free(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));
|
ui->bytesCRC32->setText(QString(digest));
|
||||||
free(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));
|
ui->bytesEntropy->setText(QString(digest));
|
||||||
free(digest);
|
free(digest);
|
||||||
rz_core_seek(core, old_offset, true);
|
rz_core_seek(core, old_offset, true);
|
||||||
|
@ -28,6 +28,7 @@ private:
|
|||||||
const QRegularExpression banned = QRegularExpression(
|
const QRegularExpression banned = QRegularExpression(
|
||||||
QStringLiteral("\\A(\\w\\.)*(system|strcpy|strcpyA|strcpyW|wcscpy|_tcscpy|_mbscpy|"
|
QStringLiteral("\\A(\\w\\.)*(system|strcpy|strcpyA|strcpyW|wcscpy|_tcscpy|_mbscpy|"
|
||||||
"StrCpy|StrCpyA|StrCpyW|lstrcpy|lstrcpyA|lstrcpyW"
|
"StrCpy|StrCpyA|StrCpyW|lstrcpy|lstrcpyA|lstrcpyW"
|
||||||
|
"DCIEnum|DCIOpenProvider|DCISendCommand|DCIBeginAccess"
|
||||||
"|_tccpy|_mbccpy|_ftcscpy|strcat|strcatA|strcatW|wcscat|_tcscat|_mbscat|"
|
"|_tccpy|_mbccpy|_ftcscpy|strcat|strcatA|strcatW|wcscat|_tcscat|_mbscat|"
|
||||||
"StrCat|StrCatA|StrCatW|lstrcat|lstrcatA|"
|
"StrCat|StrCatA|StrCatW|lstrcat|lstrcatA|"
|
||||||
"lstrcatW|StrCatBuff|StrCatBuffA|StrCatBuffW|StrCatChainW|_tccat|_"
|
"lstrcatW|StrCatBuff|StrCatBuffA|StrCatBuffW|StrCatChainW|_tccat|_"
|
||||||
@ -51,7 +52,7 @@ private:
|
|||||||
"ui64tow|_ultoa|_ultot|_ultow|CharToOem|CharToOemA|CharToOemW|"
|
"ui64tow|_ultoa|_ultot|_ultow|CharToOem|CharToOemA|CharToOemW|"
|
||||||
"OemToChar|OemToCharA|OemToCharW|CharToOemBuffA|CharToOemBuffW|alloca|_"
|
"OemToChar|OemToCharA|OemToCharW|CharToOemBuffA|CharToOemBuffW|alloca|_"
|
||||||
"alloca|strlen|wcslen|_mbslen|_mbstrlen|StrLen|lstrlen|"
|
"alloca|strlen|wcslen|_mbslen|_mbstrlen|StrLen|lstrlen|"
|
||||||
"ChangeWindowMessageFilter)\\z"));
|
"ChangeWindowMessageFilter|ChangeWindowMessageFilterEx)\\z"));
|
||||||
QList<ImportDescription> imports;
|
QList<ImportDescription> imports;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
|
||||||
/* Disassembly/Graph/Hexdump/Decompiler view priority */
|
/* 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
|
class CUTTER_EXPORT MemoryDockWidget : public AddressableDockWidget
|
||||||
{
|
{
|
||||||
|
@ -7,10 +7,16 @@ QuickFilterView::QuickFilterView(QWidget *parent, bool defaultOn)
|
|||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
debounceTimer = new QTimer(this);
|
||||||
|
debounceTimer->setSingleShot(true);
|
||||||
|
|
||||||
connect(ui->closeFilterButton, &QAbstractButton::clicked, this, &QuickFilterView::closeFilter);
|
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,
|
connect(ui->filterLineEdit, &QLineEdit::textChanged, this,
|
||||||
[this](const QString &text) { emit filterTextChanged(text); });
|
[this](const QString &text) { debounceTimer->start(150); });
|
||||||
|
|
||||||
if (!defaultOn) {
|
if (!defaultOn) {
|
||||||
closeFilter();
|
closeFilter();
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class QuickFilterView;
|
class QuickFilterView;
|
||||||
@ -31,6 +32,7 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::QuickFilterView> ui;
|
std::unique_ptr<Ui::QuickFilterView> ui;
|
||||||
|
QTimer *debounceTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QUICKFILTERVIEW_H
|
#endif // QUICKFILTERVIEW_H
|
||||||
|
@ -254,6 +254,7 @@ void SearchWidget::refreshSearchspaces()
|
|||||||
ui->searchspaceCombo->clear();
|
ui->searchspaceCombo->clear();
|
||||||
ui->searchspaceCombo->addItem(tr("asm code"), QVariant("/acj"));
|
ui->searchspaceCombo->addItem(tr("asm code"), QVariant("/acj"));
|
||||||
ui->searchspaceCombo->addItem(tr("string"), QVariant("/j"));
|
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("hex string"), QVariant("/xj"));
|
||||||
ui->searchspaceCombo->addItem(tr("ROP gadgets"), QVariant("/Rj"));
|
ui->searchspaceCombo->addItem(tr("ROP gadgets"), QVariant("/Rj"));
|
||||||
ui->searchspaceCombo->addItem(tr("32bit value"), QVariant("/vj"));
|
ui->searchspaceCombo->addItem(tr("32bit value"), QVariant("/vj"));
|
||||||
@ -301,13 +302,16 @@ void SearchWidget::updatePlaceholderText(int index)
|
|||||||
case 1: // string
|
case 1: // string
|
||||||
ui->filterLineEdit->setPlaceholderText("foobar");
|
ui->filterLineEdit->setPlaceholderText("foobar");
|
||||||
break;
|
break;
|
||||||
case 2: // hex string
|
case 2: // string (case insensitive)
|
||||||
|
ui->filterLineEdit->setPlaceholderText("FooBar");
|
||||||
|
break;
|
||||||
|
case 3: // hex string
|
||||||
ui->filterLineEdit->setPlaceholderText("deadbeef");
|
ui->filterLineEdit->setPlaceholderText("deadbeef");
|
||||||
break;
|
break;
|
||||||
case 3: // ROP gadgets
|
case 4: // ROP gadgets
|
||||||
ui->filterLineEdit->setPlaceholderText("pop,,pop");
|
ui->filterLineEdit->setPlaceholderText("pop,,pop");
|
||||||
break;
|
break;
|
||||||
case 4: // 32bit value
|
case 5: // 32bit value
|
||||||
ui->filterLineEdit->setPlaceholderText("0xdeadbeef");
|
ui->filterLineEdit->setPlaceholderText("0xdeadbeef");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -119,8 +119,9 @@ void SimpleTextGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block, b
|
|||||||
|
|
||||||
p.setPen(palette().color(QPalette::WindowText));
|
p.setPen(palette().color(QPalette::WindowText));
|
||||||
// Render node text
|
// Render node text
|
||||||
auto x = block.x + padding;
|
QFontMetrics fm = QFontMetrics(p.font());
|
||||||
int y = block.y + padding + p.fontMetrics().ascent();
|
auto x = block.x + padding / 2;
|
||||||
|
int y = block.y + padding / 2 + fm.ascent();
|
||||||
p.drawText(QPoint(x, y), content.text);
|
p.drawText(QPoint(x, y), content.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,6 +259,10 @@ void TypesWidget::showTypesContextMenu(const QPoint &pt)
|
|||||||
|
|
||||||
void TypesWidget::on_actionExport_Types_triggered()
|
void TypesWidget::on_actionExport_Types_triggered()
|
||||||
{
|
{
|
||||||
|
char *str = rz_core_types_as_c_all(Core()->core(), true);
|
||||||
|
if (!str) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
QString filename =
|
QString filename =
|
||||||
QFileDialog::getSaveFileName(this, tr("Save File"), Config()->getRecentFolder());
|
QFileDialog::getSaveFileName(this, tr("Save File"), Config()->getRecentFolder());
|
||||||
if (filename.isEmpty()) {
|
if (filename.isEmpty()) {
|
||||||
@ -272,8 +276,8 @@ void TypesWidget::on_actionExport_Types_triggered()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QTextStream fileOut(&file);
|
QTextStream fileOut(&file);
|
||||||
// TODO: use API for `tc` command once available
|
fileOut << str;
|
||||||
fileOut << Core()->cmd("tc");
|
free(str);
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +293,6 @@ void TypesWidget::on_actionLoad_New_Types_triggered()
|
|||||||
TypesInteractionDialog dialog(this);
|
TypesInteractionDialog dialog(this);
|
||||||
connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);
|
connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);
|
||||||
dialog.setWindowTitle(tr("Load New Types"));
|
dialog.setWindowTitle(tr("Load New Types"));
|
||||||
dialog.setTypeName(t.type);
|
|
||||||
dialog.exec();
|
dialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,8 +21,7 @@ VisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent)
|
|||||||
graphicsView(new QGraphicsView),
|
graphicsView(new QGraphicsView),
|
||||||
seekGraphicsItem(nullptr),
|
seekGraphicsItem(nullptr),
|
||||||
PCGraphicsItem(nullptr),
|
PCGraphicsItem(nullptr),
|
||||||
main(main),
|
main(main)
|
||||||
stats(nullptr, rz_core_analysis_stats_free)
|
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent);
|
Q_UNUSED(parent);
|
||||||
|
|
||||||
@ -119,7 +118,7 @@ void VisualNavbar::fetchStats()
|
|||||||
|
|
||||||
RzCoreLocked core(Core());
|
RzCoreLocked core(Core());
|
||||||
stats.reset(nullptr);
|
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) {
|
if (!list) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -127,7 +126,7 @@ void VisualNavbar::fetchStats()
|
|||||||
RzIOMap *map;
|
RzIOMap *map;
|
||||||
ut64 from = UT64_MAX;
|
ut64 from = UT64_MAX;
|
||||||
ut64 to = 0;
|
ut64 to = 0;
|
||||||
CutterRzListForeach (list, iter, RzIOMap, map) {
|
CutterRzListForeach (list.get(), iter, RzIOMap, map) {
|
||||||
ut64 f = rz_itv_begin(map->itv);
|
ut64 f = rz_itv_begin(map->itv);
|
||||||
ut64 t = rz_itv_end(map->itv);
|
ut64 t = rz_itv_end(map->itv);
|
||||||
if (f < from) {
|
if (f < from) {
|
||||||
@ -137,7 +136,6 @@ void VisualNavbar::fetchStats()
|
|||||||
to = t;
|
to = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rz_list_free(list);
|
|
||||||
to--; // rz_core_analysis_get_stats takes inclusive ranges
|
to--; // rz_core_analysis_get_stats takes inclusive ranges
|
||||||
if (to < from) {
|
if (to < from) {
|
||||||
return;
|
return;
|
||||||
|
@ -47,7 +47,7 @@ private:
|
|||||||
QGraphicsRectItem *PCGraphicsItem;
|
QGraphicsRectItem *PCGraphicsItem;
|
||||||
MainWindow *main;
|
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 statsWidth = 0;
|
||||||
unsigned int previousWidth = 0;
|
unsigned int previousWidth = 0;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user