├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── CODEOWNERS ├── LICENSE ├── LICENSE-THIRD-PARTY ├── README.md ├── SECURITY.md ├── deps ├── CMakeLists.txt └── retdec │ └── CMakeLists.txt ├── doc ├── CMakeLists.txt └── user_guide │ ├── CMakeLists.txt │ ├── figures │ ├── action-cut.png │ ├── action.png │ ├── addons.png │ ├── avast-logo-cmyk.eps │ ├── context-actions-1-cut.png │ ├── context-actions-1.png │ ├── context-actions-2-cut.png │ ├── context-actions-2.png │ ├── full-decompilation.png │ ├── help-about-program-cut.png │ ├── help-about-program.png │ ├── ida-output-window-highlighted.png │ ├── ida-output-window.png │ ├── menu-config-cut.png │ ├── menu-config.png │ ├── navigation.pdf │ ├── occurrence-highlighting.png │ ├── overview.png │ ├── plugin-info.png │ └── selective-decompilation.png │ ├── user_guide.pdf │ └── user_guide.tex ├── scripts ├── CMakeLists.txt ├── idc │ ├── retdec-decompile-full.idc │ └── retdec-decompile-selective.idc └── run-ida-decompilation.py └── src ├── CMakeLists.txt └── idaplugin ├── CMakeLists.txt ├── config.cpp ├── config.h ├── decompiler-config.json ├── function.cpp ├── function.h ├── place.cpp ├── place.h ├── retdec.cpp ├── retdec.h ├── token.cpp ├── token.h ├── ui.cpp ├── ui.h ├── utils.cpp ├── utils.h ├── yx.cpp └── yx.h /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | /build/ 3 | build-doc/ 4 | /.cproject 5 | /.settings/ 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## dev 4 | 5 | ## v1.0 (August 18, 2020) 6 | 7 | * Enhancement: The plugin is now a stand-alone package - i.e. a separate RetDec installation is not required ([#8](https://github.com/avast/retdec-idaplugin/issues/8)). There are no longer any external process launches ([#37](https://github.com/avast/retdec-idaplugin/issues/37), [#40](https://github.com/avast/retdec-idaplugin/issues/40), [#56](https://github.com/avast/retdec-idaplugin/issues/56), [#58](https://github.com/avast/retdec-idaplugin/issues/58), [#59](https://github.com/avast/retdec-idaplugin/issues/59), [#60](https://github.com/avast/retdec-idaplugin/issues/60)). 8 | * Enhancement: Allow arm64 decompilation. 9 | * Enhancement: RetDec JSON output's ASM to C mapping is used to make the plugin address-aware ([#49](https://github.com/avast/retdec-idaplugin/issues/49)). 10 | * Enhancement: RetDec's JSON output is used for syntax highlighting ([#48](https://github.com/avast/retdec-idaplugin/issues/48)). 11 | * Enhancement: Improve GUI interactions. 12 | * Fix: Show the decompilation windows always next to a disassembly IDA view window ([#12](https://github.com/avast/retdec-idaplugin/issues/12)). 13 | 14 | ## v0.9 (March 19, 2019) 15 | 16 | * Allow x86-64 decompilation. 17 | 18 | ## v0.8 (January 21, 2018) 19 | 20 | * Added a 64-bit address space version of RetDec IDA plugin ([#21](https://github.com/avast/retdec-idaplugin/issues/21)). 21 | * Added support for decompilation of Mach-O binary files ([#11](https://github.com/avast/retdec-idaplugin/issues/11)). 22 | 23 | ## v0.7 (September 5, 2018) 24 | 25 | * Fix: Fix Python interpreter execution via `py -3` command ([#33](https://github.com/avast/retdec-idaplugin/pull/33)). 26 | * Fix: Add Python interpreter checks and make it possible to configure path to the Python interpreter to use ([#29](https://github.com/avast/retdec-idaplugin/issues/29#issuecomment-417363723)). 27 | 28 | ## v0.6 (August 16, 2018) 29 | 30 | * Updated plugin in order to work with the new Python implementation of RetDec scripts. 31 | * Added support for macOS ([#27](https://github.com/avast/retdec-idaplugin/pull/27)). 32 | * Fix: Upgrade to a new JsonCpp API ([#28](https://github.com/avast/retdec-idaplugin/pull/28)). 33 | * Fix: Terminate spawned decompilation process when IDA is closed or a new decompilation is triggered ([#20](https://github.com/avast/retdec-idaplugin/issues/20)). 34 | * Fix: Decode and decompile only the selected function ([#6](https://github.com/avast/retdec-idaplugin/issues/6)). 35 | 36 | ## v0.5 (June 7, 2018) 37 | 38 | * Added support for IDA 7.x ([#2](https://github.com/avast/retdec-idaplugin/issues/2)). Dropped support for IDA 6.x. 39 | * Removed remote decompilation through RetDec's API ([#9](https://github.com/avast/retdec-idaplugin/issues/9), [#13](https://github.com/avast/retdec-idaplugin/issues/13)). 40 | * Fix: `#!/usr/bin/env bash` is now used instead of `#!/bin/bash` to run the scripts ([avast/retdec #258](https://github.com/avast/retdec/issues/258)). 41 | * Fix: Allow decompilation of input files with `f_LOADER` file type ([#3](https://github.com/avast/retdec-idaplugin/issues/3)). 42 | 43 | ## v0.4 (December 14, 2017) 44 | 45 | * Added support for local decompilations. Requires locally installed [RetDec](https://github.com/avast/retdec). 46 | 47 | ## v0.3.1 (September 7, 2016) 48 | 49 | * Fixed searching for the input file in cases when the `.idb` file was moved. 50 | * The plugin now creates its temporary files in the directory with the `.idb` file, not in the directory with the input file. 51 | 52 | ## v0.3 (August 22, 2016) 53 | 54 | * Added support for decompilation of COFF object files. 55 | * Added support for decompilation of Intel HEX files. 56 | * Added support for decompilation of files containing just raw machine code. 57 | * Added generation of IDA comments into C files generated by full decompilation. 58 | * The plugin no longer performs automatic decompilation of the opened file upon the start of IDA. 59 | * The plugin no longer ignores statically linked functions when selective decompilation is invoked through IDA. 60 | * The plugin does not allow decompilation of non-existing files like objects extracted from archives. 61 | * The plugin does not allow decompilations that it cannot properly handle. 62 | 63 | ## v0.2 (January 26, 2016) 64 | 65 | * Added an automatic check whether a newer version of the plugin is available. When a newer version exists, the user is prompted to download and install the latest version. 66 | * The output from full decompilations is no longer syntax-highlighted with IDA's color tags. 67 | * Several bug fixes. 68 | 69 | ## v0.1.1 (November 25, 2015) 70 | 71 | * The plugin requires fewer third-party libraries. 72 | 73 | ## v0.1 (November 20, 2015) 74 | 75 | * Initial version. 76 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | 3 | project(retdec-idaplugin CXX C) 4 | set(RELEASE_VERSION "1.0-ida80") 5 | 6 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 7 | 8 | option(RETDEC_IDAPLUGIN_DOC "Build the documentation." OFF) 9 | 10 | # Set the default build type to 'Release' 11 | if(NOT CMAKE_BUILD_TYPE) 12 | set(default_build_type "Release") 13 | message(STATUS "Setting build type to '${default_build_type}' as none was specified.") 14 | set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) 15 | endif() 16 | 17 | set(CMAKE_CXX_STANDARD 17) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | set(CMAKE_CXX_EXTENSIONS OFF) 20 | set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 21 | 22 | # Check that obligatory parameters were defined. 23 | if(NOT IDA_SDK_DIR) 24 | message(FATAL_ERROR "Path to IDA SDK was not specified. Use -DIDA_SDK_DIR=.") 25 | endif() 26 | if(NOT EXISTS "${IDA_SDK_DIR}") 27 | message(FATAL_ERROR "Specified IDA SDK path does not exist.") 28 | endif() 29 | 30 | # Build parameters. 31 | if(MSVC) # Windows 32 | # Disable warnings (there are too many of them, including warnings from 33 | # third-party libraries, which cannot be selectively disabled when using MSVC). 34 | string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 35 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0") 36 | 37 | # Disable the min() and max() macros to prevent errors when using e.g. 38 | # std::numeric_limits<...>::max() 39 | # (http://stackoverflow.com/questions/1904635/warning-c4003-and-errors-c2589-and-c2059-on-x-stdnumeric-limitsintmax). 40 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DNOMINMAX") 41 | 42 | # Force static runtime 43 | string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) 44 | string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) 45 | string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) 46 | string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO}) 47 | string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_MINSIZEREL ${CMAKE_CXX_FLAGS_MINSIZEREL}) 48 | string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_MINSIZEREL ${CMAKE_C_FLAGS_MINSIZEREL}) 49 | string(REPLACE "/MDd" "/MT" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) 50 | string(REPLACE "/MDd" "/MT" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) 51 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MT") 52 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MT") 53 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 54 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MT") 55 | set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MT") 56 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MT") 57 | set(RETDEC_MSVC_STATIC_RUNTIME ON) 58 | elseif(UNIX) # Linux or macOS 59 | # Common options. 60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 61 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic") 62 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") 63 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") 64 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") 65 | 66 | # Ignore the following warnings (they are not fixable). 67 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format") 68 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") 69 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-non-virtual-dtor") 70 | 71 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") 72 | else() 73 | message(FATAL_ERROR "Unsupported system type: ${CMAKE_SYSTEM_NAME}") 74 | endif() 75 | 76 | # Global defines. 77 | add_definitions(-D__IDP__ -D__PLUGIN__ -DNO_OBSOLETE_FUNCS) 78 | add_definitions(-D__X64__) 79 | add_definitions(-DRELEASE_VERSION="${RELEASE_VERSION}") 80 | if(WIN32) 81 | add_definitions(-D__NT__) 82 | elseif(APPLE) 83 | add_definitions(-D__MAC__) 84 | elseif(UNIX) # APPLE is also UNIX, so it MUST be before this elseif(). 85 | add_definitions(-D__LINUX__) 86 | else() 87 | message(FATAL_ERROR "Unsupported system type: ${CMAKE_SYSTEM_NAME}") 88 | endif() 89 | 90 | # Subdirectories. 91 | add_subdirectory(deps) 92 | if(RETDEC_IDAPLUGIN_DOC) 93 | add_subdirectory(doc) 94 | endif() 95 | add_subdirectory(scripts) 96 | add_subdirectory(src) 97 | 98 | # Create release. 99 | if(RETDEC_IDAPLUGIN_DOC) 100 | set(RELEASE_DIR_NAME "retdec-idaplugin") 101 | set(RELEASE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${RELEASE_DIR_NAME}") 102 | set(RELEASE_RESOURCES_DIR "${RELEASE_DIR}/retdec") 103 | if(MSVC) # Windows 104 | set(RELEASE_OS_NAME "windows") 105 | elseif(APPLE) # macOS 106 | set(RELEASE_OS_NAME "macOS") 107 | else() # Linux 108 | set(RELEASE_OS_NAME "linux") 109 | endif() 110 | add_custom_target(release 111 | DEPENDS user-guide idaplugin32 idaplugin64 112 | # Create directory structure. 113 | COMMAND ${CMAKE_COMMAND} -E make_directory "${RELEASE_DIR}" 114 | COMMAND ${CMAKE_COMMAND} -E make_directory "${RELEASE_RESOURCES_DIR}" 115 | # Copy plugins. 116 | COMMAND ${CMAKE_COMMAND} -E copy "$" "${RELEASE_DIR}" 117 | COMMAND ${CMAKE_COMMAND} -E copy "$" "${RELEASE_DIR}" 118 | # Copy resources. 119 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/README.md" "${RELEASE_RESOURCES_DIR}" 120 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/CHANGELOG.md" "${RELEASE_RESOURCES_DIR}" 121 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/LICENSE" "${RELEASE_RESOURCES_DIR}" 122 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/LICENSE-THIRD-PARTY" "${RELEASE_RESOURCES_DIR}" 123 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/doc/user_guide/user_guide.pdf" "${RELEASE_RESOURCES_DIR}" 124 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/src/idaplugin/decompiler-config.json" "${RELEASE_RESOURCES_DIR}" 125 | COMMAND ${CMAKE_COMMAND} -E copy_directory "${retdec_SOURCE_DIR}/support/ordinals" "${RELEASE_RESOURCES_DIR}/ordinals/" 126 | COMMAND ${CMAKE_COMMAND} -E copy_directory "${retdec_SOURCE_DIR}/support/yara_patterns" "${RELEASE_RESOURCES_DIR}/yara_patterns/" 127 | COMMAND ${CMAKE_COMMAND} -E copy_directory "${retdec_SOURCE_DIR}/support/types" "${RELEASE_RESOURCES_DIR}/types/" 128 | # Create the archive. 129 | COMMAND ${CMAKE_COMMAND} -E tar "cvf" "${CMAKE_CURRENT_BINARY_DIR}/${RELEASE_DIR_NAME}-v${RELEASE_VERSION}-${RELEASE_OS_NAME}.zip" --format=zip "${RELEASE_DIR}" 130 | ) 131 | endif() 132 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @PeterMatula 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Avast Software 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE-THIRD-PARTY: -------------------------------------------------------------------------------- 1 | RetDec idaplugin uses third-party libraries or other resources that may be 2 | distributed under licenses different than this software. 3 | 4 | In the event that we accidentally failed to list a required notice, 5 | please bring it to our attention by contacting the repository owner. 6 | 7 | RetDec idaplugin uses the following third-party libraries or other resources: 8 | 1) RetDec: https://github.com/avast/retdec 9 | 10 | These third-party libraries or other resources are licensed under the 11 | following licenses: 12 | 13 | =============================================================================== 14 | 1) RetDec 15 | =============================================================================== 16 | 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2020 Avast Software 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy of 22 | this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights to 24 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 25 | of the Software, and to permit persons to whom the Software is furnished to do 26 | so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RetDec IDA plugin 2 | 3 | RetDec plugin for IDA (Interactive Disassembler). 4 | 5 | The plugin is compatible with the IDA 7.5+ versions. 6 | The plugin does NOT work with IDA 6.x, IDA 7.0-7.4, or freeware version of IDA 7.0. 7 | The plugin comes at both 32-bit and 64-bit address space variants (both are 64-bit binaries). I.e. it works in both `ida` and `ida64`. 8 | At the moment, it can decompile the following architectures: 9 | * 32-bit: x86, arm, mips, and powerpc. 10 | * 64-bit: x86-64, arm64. 11 | 12 | ## Installation and Use 13 | 14 | Currently, we officially support only Windows and Linux. It may be possible to build macOS version from the sources, but since we do not own a macOS version of IDA, we cannot create a pre-built package, or continually make sure the macOS build is not broken. 15 | 16 | 1. Either download and unpack a pre-built package from the [latest release](https://github.com/avast/retdec-idaplugin/releases/latest), or build and install the RetDec IDA plugin by yourself (the process is described below). 17 | 2. Follow the user guide (`user_guide.pdf`) that is part of the downloaded package, or use the [current version](https://github.com/avast/retdec-idaplugin/blob/master/doc/user_guide/user_guide.pdf) from this repository. 18 | 3. Don't forget to install the required dependencies mentioned in the user guide. 19 | 20 | ## Build and Installation 21 | 22 | ### Requirements 23 | 24 | **Note: These are requirements to build the RetDec IDA plugin, not to run it. See our [User Guide](https://github.com/avast/retdec-idaplugin/blob/master/doc/user_guide/user_guide.pdf) for information on plugin installation, configuration, and use.** 25 | 26 | * A compiler supporting C++17 27 | * On Windows, only Microsoft Visual C++ is supported (version >= Visual Studio 2017). 28 | * CMake (version >= 3.6) 29 | * IDA SDK (version >= 7.7) 30 | 31 | ### Process 32 | 33 | * Clone the repository: 34 | * `git clone https://github.com/avast/retdec-idaplugin.git` 35 | * Linux: 36 | * `cd retdec-idaplugin` 37 | * `mkdir build && cd build` 38 | * `cmake .. -DIDA_SDK_DIR=` 39 | * `make` 40 | * `make install` (if `IDA_DIR` was set, see below) 41 | * Windows: 42 | * Open a command prompt (e.g. `C:\msys64\msys2_shell.cmd` from [MSYS2](https://github.com/avast/retdec/wiki/Windows-Environment)) 43 | * `cd retdec-idaplugin` 44 | * `mkdir build && cd build` 45 | * `cmake .. -DIDA_SDK_DIR= -G` 46 | * `cmake --build . --config Release -- -m` 47 | * `cmake --build . --config Release --target install` (if `IDA_DIR` was set, see below) 48 | * Alternatively, you can open `retdec-idaplugin.sln` generated by `cmake` in Visual Studio IDE. 49 | 50 | You must pass the following parameters to `cmake`: 51 | * `-DIDA_SDK_DIR=` to tell `cmake` where the IDA SDK directory is located. 52 | * (Windows only) `-G` is `-G"Visual Studio 15 2017 Win64"` for 64-bit build using Visual Studio 2017. Later versions of Visual Studio may be used. Only 64-bit build is supported. 53 | 54 | You can pass the following additional parameters to `cmake`: 55 | * `-DIDA_DIR=` to tell `cmake` where to install the plugin. If specified, installation will copy plugin binaries into `IDA_DIR/plugins`, and content of `scripts/idc` directory into `IDA_DIR/idc`. If not set, installation step does nothing. 56 | * `-DRETDEC_IDAPLUGIN_DOC=ON` to enable the `user-guide` target which generates the user guide document (disabled by default, the target needs to be explicitly invoked). 57 | 58 | ## User Guide 59 | 60 | The [User Guide](https://github.com/avast/retdec-idaplugin/blob/master/doc/user_guide/user_guide.pdf) in a PDF form is located in `doc/user_guide/user_guide.pdf`. 61 | 62 | You can build your own guide by enabling and invoking the `user-guide` target: 63 | * `cmake .. -DRETDEC_IDAPLUGIN_DOC=ON` 64 | * Linux: `make user-guide` 65 | * Windows: `cmake --build . --config Release --target user-guide` 66 | * Requires [LaTeX](https://www.latex-project.org/), LaTeX packages, and related tools. 67 | * The resulting PDF will overwrite the original `user_guide.pdf` in `doc/user_guide`. 68 | 69 | ## License 70 | 71 | Copyright (c) 2020 Avast Software, licensed under the MIT license. See the `LICENSE` file for more details. 72 | 73 | RetDec IDA plugin uses third-party libraries or other resources listed, along with their licenses, in the `LICENSE-THIRD-PARTY` file. 74 | 75 | ## Contributing 76 | 77 | See [RetDec contribution guidelines](https://github.com/avast/retdec/wiki/Contribution-Guidelines). 78 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | Please use [this submission form](https://www.nortonlifelock.com/us/en/contact-us/report-a-security-vulnerability/) to report any (potential) security vulnerabilities. Provide as much details as possible. 2 | -------------------------------------------------------------------------------- /deps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_subdirectory(retdec) 3 | 4 | set(retdec_SOURCE_DIR ${retdec_SOURCE_DIR} PARENT_SCOPE) 5 | -------------------------------------------------------------------------------- /deps/retdec/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | if(RETDEC_LOCAL_DIR) 3 | message(STATUS "RetDec: using local RetDec directory.") 4 | add_subdirectory( 5 | ${RETDEC_LOCAL_DIR} 6 | ${CMAKE_CURRENT_BINARY_DIR}/retdec-build 7 | EXCLUDE_FROM_ALL 8 | ) 9 | set(retdec_SOURCE_DIR ${RETDEC_LOCAL_DIR}) 10 | else() 11 | message(STATUS "RetDec: using remote RetDec revision.") 12 | include(FetchContent) 13 | 14 | FetchContent_Declare(retdec 15 | GIT_REPOSITORY https://github.com/avast/retdec 16 | GIT_TAG 0749a46b2490c8d499d64f08629271e16c311d82 17 | ) 18 | 19 | FetchContent_GetProperties(retdec) 20 | if(NOT retdec_POPULATED) 21 | FetchContent_Populate(retdec) 22 | add_subdirectory( 23 | ${retdec_SOURCE_DIR} 24 | ${retdec_BINARY_DIR} 25 | EXCLUDE_FROM_ALL 26 | ) 27 | endif() 28 | endif() 29 | 30 | if(IDA_DIR) 31 | install( 32 | DIRECTORY 33 | "${retdec_SOURCE_DIR}/support/ordinals" 34 | "${retdec_SOURCE_DIR}/support/yara_patterns" 35 | "${retdec_SOURCE_DIR}/support/types" 36 | DESTINATION 37 | "${IDA_DIR}/plugins/retdec/" 38 | ) 39 | endif() 40 | 41 | set(retdec_SOURCE_DIR ${retdec_SOURCE_DIR} PARENT_SCOPE) 42 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(user_guide) 2 | -------------------------------------------------------------------------------- /doc/user_guide/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## CMake build script for user guide. 2 | ## 3 | 4 | find_package(LATEX REQUIRED) 5 | find_package(LATEX COMPONENTS PDFLATEX REQUIRED) 6 | 7 | # Configure and copy LaTeX source file. 8 | configure_file("user_guide.tex" "${CMAKE_CURRENT_BINARY_DIR}/user_guide.tex" @ONLY) 9 | 10 | add_custom_target(user-guide 11 | SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/user_guide.tex 12 | COMMENT "Generating user guide with LaTeX" VERBATIM 13 | # We need to copy all the sources to the working directory (CMAKE_CURRENT_BINARY_DIR). 14 | # Tha LaTeX source file is copied using configure_file(). 15 | COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/figures" "${CMAKE_CURRENT_BINARY_DIR}/figures" 16 | # Compile the sources using pdflatex. 17 | COMMAND ${PDFLATEX_COMPILER} -draftmode user_guide.tex 18 | COMMAND ${PDFLATEX_COMPILER} user_guide.tex 19 | # Copy the result back to the source directory. 20 | COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf" "${CMAKE_CURRENT_SOURCE_DIR}") 21 | -------------------------------------------------------------------------------- /doc/user_guide/figures/action-cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/action-cut.png -------------------------------------------------------------------------------- /doc/user_guide/figures/action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/action.png -------------------------------------------------------------------------------- /doc/user_guide/figures/addons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/addons.png -------------------------------------------------------------------------------- /doc/user_guide/figures/avast-logo-cmyk.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/avast-logo-cmyk.eps -------------------------------------------------------------------------------- /doc/user_guide/figures/context-actions-1-cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/context-actions-1-cut.png -------------------------------------------------------------------------------- /doc/user_guide/figures/context-actions-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/context-actions-1.png -------------------------------------------------------------------------------- /doc/user_guide/figures/context-actions-2-cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/context-actions-2-cut.png -------------------------------------------------------------------------------- /doc/user_guide/figures/context-actions-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/context-actions-2.png -------------------------------------------------------------------------------- /doc/user_guide/figures/full-decompilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/full-decompilation.png -------------------------------------------------------------------------------- /doc/user_guide/figures/help-about-program-cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/help-about-program-cut.png -------------------------------------------------------------------------------- /doc/user_guide/figures/help-about-program.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/help-about-program.png -------------------------------------------------------------------------------- /doc/user_guide/figures/ida-output-window-highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/ida-output-window-highlighted.png -------------------------------------------------------------------------------- /doc/user_guide/figures/ida-output-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/ida-output-window.png -------------------------------------------------------------------------------- /doc/user_guide/figures/menu-config-cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/menu-config-cut.png -------------------------------------------------------------------------------- /doc/user_guide/figures/menu-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/menu-config.png -------------------------------------------------------------------------------- /doc/user_guide/figures/navigation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/navigation.pdf -------------------------------------------------------------------------------- /doc/user_guide/figures/occurrence-highlighting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/occurrence-highlighting.png -------------------------------------------------------------------------------- /doc/user_guide/figures/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/overview.png -------------------------------------------------------------------------------- /doc/user_guide/figures/plugin-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/plugin-info.png -------------------------------------------------------------------------------- /doc/user_guide/figures/selective-decompilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/figures/selective-decompilation.png -------------------------------------------------------------------------------- /doc/user_guide/user_guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avast/retdec-idaplugin/dc7b0a2a0d31221dc4b14d5543489a72659b784d/doc/user_guide/user_guide.pdf -------------------------------------------------------------------------------- /doc/user_guide/user_guide.tex: -------------------------------------------------------------------------------- 1 | \documentclass[pdftex, a4paper,12pt, oneside, svgnames]{article} 2 | 3 | \usepackage[usenames,dvipsnames,svgnames,table]{xcolor} 4 | \usepackage[left=3cm,text={15.25cm,23cm},top=3cm]{geometry} 5 | \usepackage[T1]{fontenc} 6 | \usepackage[utf8]{inputenc} 7 | 8 | \usepackage{pslatex} % to use PostScript fonts 9 | \usepackage[pdftex]{graphicx} 10 | 11 | \usepackage{listings} 12 | \usepackage{enumerate} 13 | \usepackage{epsfig} 14 | \usepackage{amsfonts} 15 | \usepackage{amsmath} 16 | \usepackage[outline]{contour} 17 | \usepackage{fancyhdr} 18 | \usepackage[center]{caption} 19 | 20 | \catcode`\_=12 % no need to escape (\_) underscores, isn't it dangerous? 21 | 22 | \usepackage{url} 23 | \usepackage{hyperref} 24 | \hypersetup{ 25 | colorlinks, 26 | linkcolor={black}, 27 | citecolor={green}, 28 | urlcolor={blue} 29 | } 30 | 31 | \usepackage{amssymb} % http://ctan.org/pkg/amssymb 32 | \usepackage{pifont} % http://ctan.org/pkg/pifont 33 | \newcommand{\cmark}{\ding{51}}% 34 | \newcommand{\xmark}{\ding{55}}% 35 | 36 | \makeatletter 37 | \newcommand\HUGE{\@setfontsize\Huge{50}{60}} 38 | \makeatother 39 | 40 | \renewcommand*\familydefault{cmss} 41 | 42 | \begin{document} 43 | 44 | %----------------------------------------------------------------------------- 45 | % Title page 46 | %----------------------------------------------------------------------------- 47 | 48 | \begin{titlepage} 49 | \begin{center} 50 | 51 | \fontfamily{cmss} 52 | 53 | \includegraphics[width=12cm]{figures/avast-logo-cmyk} 54 | 55 | \vfill 56 | 57 | {\LARGE Retargetable Decompiler's IDA Plugin} 58 | \\[5 mm] 59 | {\HUGE User Guide} 60 | \\[3 mm] 61 | {\large Version @RELEASE_VERSION@} 62 | 63 | \vfill 64 | 65 | {\LARGE 66 | \href{https://github.com/avast/retdec-idaplugin}{https://github.com/avast/retdec-idaplugin} \\[2 mm] 67 | \href{https://retdec.com}{https://retdec.com} \\[2 mm] 68 | \href{mailto:support@retdec.com}{support@retdec.com} \\[4 mm] 69 | \today} 70 | 71 | \end{center} 72 | \end{titlepage} 73 | 74 | %----------------------------------------------------------------------------- 75 | % Content 76 | %----------------------------------------------------------------------------- 77 | 78 | \tableofcontents 79 | %\listoffigures 80 | %\listoftables 81 | 82 | %----------------------------------------------------------------------------- 83 | % Introduction 84 | %----------------------------------------------------------------------------- 85 | \newpage 86 | \section{Introduction} 87 | \label{sec:introduction} 88 | This document describes the Retargetable Decompiler's plugin for IDA (RetDec plugin). Its goal is to integrate with IDA, give transparent access to the Retargetable Decompiler and provide user-interaction capabilities like navigation or code refactoring. An example of code decompiled by Retdec plugin is shown in Figure~\ref{fig:overview}. 89 | 90 | Retargetable Decompiler (RetDec) is a reverse-engineering tool independent of any particular target architecture, file format, operating system, or compiler. It was developed in cooperation of Faculty of Information Technology, Brno University of Technology and AVG Technologies. Since the acquisition of AVG Technologies by Avast in 2016, Avast has continued to develop the decompiler. It is using Capstone disassembler engine and a Capstone2LlvmIR library to translate machine code into a high-level-language representation. Currently, the decompiler supports the x86, x86-64, arm, arm64, mips, and powerpc architectures using the Windows PE, COFF, Unix ELF, macOS Mach-O, Intel HEX, and RAW binary file formats. 91 | 92 | RetDec can be used in the following ways: 93 | \begin{enumerate} 94 | \item Standalone RetDec: either compiling \href{https://github.com/avast/retdec}{RetDec repository} on your own, or downloading and installing \href{https://github.com/avast/retdec/releases}{RetDec binary release}. 95 | \item Standalone \href{https://retdec.com/idaplugin/}{RetDec IDA plugin} (this guide's topic). 96 | \end{enumerate} 97 | 98 | \begin{figure}[!ht] 99 | \centering 100 | \includegraphics[width=\textwidth]{figures/overview} 101 | \caption{Example of code decompiled by RetDec plugin.} 102 | \label{fig:overview} 103 | \end{figure} 104 | 105 | %----------------------------------------------------------------------------- 106 | % Installation 107 | %----------------------------------------------------------------------------- 108 | \newpage 109 | \section{Installation} 110 | \label{sec:installation} 111 | This section describes prerequisites and the installation process of RetDec IDA plugin binary release. 112 | 113 | It is also possible to build and install the plugin directly from sources. To do so, follow the \href{https://github.com/avast/retdec-idaplugin#build-and-installation}{Build and Installation} instructions instead of this section. 114 | 115 | \subsection{IDA} 116 | The plugin is created using IDA SDK version 8.0. The plugin is compatible with IDA versions 8.0. The plugin does NOT work with IDA 7.x, or freeware version of IDA. The plugin comes at both 32-bit and 64-bit address space variants (both are 64-bit binaries). I.e. it works in both \texttt{ida} and \texttt{ida64}. 117 | 118 | \subsection{RetDec IDA plugin} 119 | The binary release packages are available for Linux and Windows. 120 | 121 | \subsubsection{Linux} 122 | Follow the next steps to install RetDec plugin in a Linux environment: 123 | \begin{enumerate} 124 | \item Install 64-bit versions of the following shared-object dependencies: 125 | \begin{verbatim} 126 | libcrypto.so.1.1 libz.so.1 libstdc++.so.6 127 | libm.so.6 libgcc_s.so.1 libc.so.6 128 | \end{verbatim} 129 | \item Download the Linux installation package (Table~\ref{table:installation-package-linux}) from the \href{https://github.com/avast/retdec-idaplugin/releases}{project's release page}. 130 | \item Extract the package. Directory \texttt{retdec-idaplugin} gets created. 131 | \item Copy the contents of the directory, not the directory itself, to the IDA's plugins directory (\texttt{/plugins}). 132 | \end{enumerate} 133 | 134 | \begin{table}[!ht] 135 | \centering 136 | \caption{Linux installation package contents.} 137 | \label{table:installation-package-linux} 138 | \begin{tabular}{ll} 139 | \textbf{File} & \textbf{Description}\\ 140 | \hline 141 | \texttt{retdec.so} & 64-bit Linux RetDec plugin for 32-bit address space. \\ 142 | \texttt{retdec64.so} & 64-bit Linux RetDec plugin for 64-bit address space. \\ 143 | \texttt{retdec/} & Directory with RetDec resources. \\ 144 | \texttt{retdec/user_guide.pdf} & RetDec plugin's user guide (this document). \\ 145 | \texttt{retdec/LICENSE} & RetDec IDA plugin's license. \\ 146 | \texttt{retdec/LICENSE-THIRD-PARTY} & Licenses of libraries used by RetDec plugin. \\ 147 | \texttt{retdec/decompiler-config.json} & RetDec configuration file. \\ 148 | \texttt{retdec/*/} & Directories with decompilation resources. \\ 149 | \end{tabular} 150 | \end{table} 151 | 152 | \subsubsection{Windows} 153 | \label{sec:installation:windows} 154 | The Windows version of the plugin requires Windows 7 or later. Follow the next steps to install RetDec plugin in a Windows environment: 155 | \begin{enumerate} 156 | \item Install \href{https://www.openssl.org/}{OpenSSL 3}. You may use a pre-built binary release, for example from \href{https://slproweb.com/products/Win32OpenSSL.html}{here} or \href{https://kb.firedaemon.com/support/solutions/articles/4000121705-openssl-3-0-and-1-1-1-binary-distributions-for-microsoft-windows}{here}. 157 | \item Download the Windows installation package (Table~\ref{table:installation-package-windows}) from the \href{https://github.com/avast/retdec-idaplugin/releases}{project's release page}. 158 | \item Extract the package. Directory \texttt{retdec-idaplugin} gets created. 159 | \item Copy the contents of the directory, not the directory itself, to the IDA's plugins directory (\texttt{/plugins}). 160 | \end{enumerate} 161 | 162 | \begin{table}[!ht] 163 | \centering 164 | \caption{Windows installation package contents.} 165 | \label{table:installation-package-windows} 166 | \begin{tabular}{ll} 167 | \textbf{File} & \textbf{Description}\\ 168 | \hline 169 | \texttt{retdec.dll} & 64-bit Windows RetDec plugin for 32-bit address space. \\ 170 | \texttt{retdec64.dll} & 64-bit Windows RetDec plugin for 64-bit address space. \\ 171 | \texttt{retdec/} & Directory with RetDec resources. \\ 172 | \texttt{retdec/user_guide.pdf} & RetDec plugin's user guide (this document). \\ 173 | \texttt{retdec/LICENSE} & RetDec IDA plugin's license. \\ 174 | \texttt{retdec/LICENSE-THIRD-PARTY} & Licenses of libraries used by RetDec plugin. \\ 175 | \texttt{retdec/decompiler-config.json} & RetDec configuration file. \\ 176 | \texttt{retdec/support/} & Directory with decompilation resources. \\ 177 | \end{tabular} 178 | \end{table} 179 | 180 | %----------------------------------------------------------------------------- 181 | % Configuration 182 | %----------------------------------------------------------------------------- 183 | \section{Configuration} 184 | \label{sec:configuration} 185 | This section describes how to configure RetDec plugin. After you follow these steps, you should have your plugin ready for work. 186 | 187 | \subsection{IDA's plugin.cfg} 188 | \label{sec:config:plugin-cfg} 189 | The plugin's default mode is set to selective decompilation (see Section~\ref{sec:decompilation}). It tries to register hotkey \texttt{Ctrl+d} for its invocation. If you already use this hotkey for another action or you just want to use a different hotkey, you need to modify IDA's plugin configuration file. 190 | 191 | The IDA's plugin configuration file is in \texttt{/plugins/plugins.cfg}. Its format is documented inside the file itself. To configure RetDec plugin to use hotkey \texttt{Ctrl+Alt+d} instead of the default \texttt{Ctrl+d}, add the following line: 192 | \begin{verbatim} 193 | ; Plugin_name File_name Hotkey Arg 194 | ; ------------------------------------------------- 195 | Retargetable_Decompiler retdec Ctrl-Alt-d 0 196 | \end{verbatim} 197 | 198 | %----------------------------------------------------------------------------- 199 | % Plugin Information 200 | %----------------------------------------------------------------------------- 201 | \section{Plugin Information} 202 | \label{sec:plugin-info} 203 | This section describes how to find information about RetDec plugin you are currently using and how the plugin communicates information to you. 204 | 205 | \subsection{About Plugin} 206 | Information about RetDec plugin can be found among IDA's information on the registered plugins at \texttt{Help/About program} (Figure~\ref{fig:about-ida}), where you need to click on the \texttt{Addons} button (Figure~\ref{fig:addons}). Then, find the \texttt{Retargetable Decompiler} entry in the presented list (Figure~\ref{fig:plugin-info}). 207 | 208 | \begin{figure}[!ht] 209 | \centering 210 | \includegraphics[width=12cm]{figures/help-about-program-cut} 211 | \caption{Opening the About IDA dialog from the menu.} 212 | \label{fig:about-ida} 213 | \end{figure} 214 | 215 | \begin{figure}[!ht] 216 | \centering 217 | \includegraphics[width=10cm]{figures/addons} 218 | \caption{Information window about IDA.} 219 | \label{fig:addons} 220 | \end{figure} 221 | 222 | \begin{figure}[!ht] 223 | \centering 224 | \includegraphics[width=10cm]{figures/plugin-info} 225 | \caption{Information window about RetDec plugin.} 226 | \label{fig:plugin-info} 227 | \end{figure} 228 | 229 | \subsection{Output Window} 230 | Right after the start, as well as during the work with RetDec plugin, it communicates with you mainly through the IDA's output window (Figure~\ref{fig:ida-output-window}). Here, you are shown several kinds of important messages: 231 | \begin{verbatim} 232 | [RetDec info] : some important piece of information 233 | [RetDec warning]: something went a little bit wrong 234 | [RetDec error] : something went very wrong 235 | \end{verbatim} 236 | 237 | \begin{figure}[!ht] 238 | \centering 239 | \includegraphics[width=13cm]{figures/ida-output-window-highlighted} 240 | \caption{IDA's output window.} 241 | \label{fig:ida-output-window} 242 | \end{figure} 243 | 244 | \subsection{GUI Windows} 245 | Sometimes, RetDec plugin wants to be sure you noticed an important message or event. In such a case, it shows you a pop-up window, which forces you to acknowledge it by pressing \texttt{OK} or a similar button. 246 | 247 | %----------------------------------------------------------------------------- 248 | % Decompilation 249 | %----------------------------------------------------------------------------- 250 | \section{Decompilation} 251 | \label{sec:decompilation} 252 | This section describes how to invoke a decompilation. After reading it, you should be able to decompile a selected function, as well as an entire binary that is being analyzed. 253 | 254 | \subsection{Selective Decompilation} 255 | \label{sec:selective-decompilation} 256 | RetDec plugin's primary decompilation mode is selective decompilation. It decompiles the function that is currently under the cursor. It is invoked from the IDA's disassembly window, where you need to bring focus to the desired function and use either the default hotkey \texttt{Ctrl+d}, or a hotkey you configured according to Section~\ref{sec:config:plugin-cfg}. You can also trigger it by running the plugin from the main IDA menu bar (Figure~\ref{fig:selective-decompilation}). 257 | 258 | Once the decompilation is finished, the decompiled source code is displayed in a new IDA viewer window. Here, you can invoke new decompilations by double-clicking on function calls. 259 | 260 | At the moment, the RetDec plugin supports only one simultaneous decompilation. 261 | 262 | \begin{figure}[!ht] 263 | \centering 264 | \includegraphics[width=13cm]{figures/selective-decompilation} 265 | \caption{Selective decompilation.} 266 | \label{fig:selective-decompilation} 267 | \end{figure} 268 | 269 | \subsection{Full Decompilation} 270 | You can also decompile an entire binary that is being analyzed. Either use the hotkey \texttt{Ctrl+Shift+d}, or the main IDA menu bar (Figure~\ref{fig:full-decompilation}). The result of this decompilation is stored into an output file, whose location is communicated to you through IDA's output window. The result cannot be displayed in the IDA's viewer window. 271 | 272 | Same as with selective decompilation (Section~\ref{sec:selective-decompilation}), only one decompilation can run at a time. 273 | 274 | \begin{figure}[!ht] 275 | \centering 276 | \includegraphics[width=15cm]{figures/full-decompilation} 277 | \caption{Full decompilation.} 278 | \label{fig:full-decompilation} 279 | \end{figure} 280 | 281 | %----------------------------------------------------------------------------- 282 | % User Interactions 283 | %----------------------------------------------------------------------------- 284 | \section{User Interactions} 285 | \label{sec:user-interactions} 286 | This section describes various kinds of user interactions that are currently supported by RetDec plugin. As was stated in Section~\ref{sec:decompilation}, these interactions are applicable only on results from selective decompilations because full decompilations cannot be displayed in IDA's viewer window. 287 | 288 | \subsection{Basic Interactions} 289 | We use the IDA's native custom viewer window to display the decompiled source codes. Therefore, the plugin feels like part of IDA and we get a word occurrences highlighting (Figure~\ref{fig:occurrence-highlighting}) out of the box. 290 | 291 | \begin{figure}[!ht] 292 | \centering 293 | \includegraphics[width=8cm]{figures/occurrence-highlighting} 294 | \caption{Native word occurrence highlighting.} 295 | \label{fig:occurrence-highlighting} 296 | \end{figure} 297 | 298 | \subsection{Navigation} 299 | \label{sec:navigation} 300 | RetDec plugin supports function navigation---jumping forward and backward between already decompiled functions, or invoke an entirely new decompilation. When you double-click on a function call, the plugin presents the requested function. If it was already decompiled in the past, the cached result is shown to perform the action faster. You have to either explicitly request a re-decompilation of the previously processed functions, or perform an action that triggers the re-decompilation automatically (see Section~\ref{sec:refactoring}). Re-decompilation can be forced by using the selective decompilation hotkey in IDA's disassembly (re-decompilation of any function), or in the RetDec plugin's viewer window (re-decompilation of currently shown function). If the double-clicked function was not decompiled yet, it is selectively reversed and displayed. In either case, only one function is shown at a time. A navigation entry for the newly presented function is added into a doubly linked navigation list, right after the entry for function from which the invocation was made. The list is then used for forward and backward navigation between the stored functions. An example of such navigation is depicted in Figure~\ref{fig:navigation}. 301 | 302 | \begin{figure}[!ht] 303 | \centering 304 | \includegraphics[width=8cm]{figures/navigation} 305 | \caption{Decompiled function navigation example.} 306 | \label{fig:navigation} 307 | \end{figure} 308 | 309 | RetDec navigation is integrated with IDA's graphical control elements, so you can use either \contour{black}{$\Leftarrow$} and \contour{black}{$\Rightarrow$} buttons or hotkeys: 310 | \begin{itemize} 311 | \item \texttt{Esc} to move backward. 312 | \item \texttt{Ctrl+Enter} to move forward. 313 | \end{itemize} 314 | 315 | \subsection{Code Refactoring} 316 | \label{sec:refactoring} 317 | The RetDec plugin's viewer windows also allows you to refactor displayed source code. We can divide the source-code modifications into two basic categories: 318 | \begin{itemize} 319 | \item Those which do not require immediate re-decompilation, like object-identifier renaming or function-comment insertion. 320 | \item Those which automatically trigger re-decompilation of the modified function. These are typically changes that can be used or propagated by reversing analyses. For example, a user-specified object data type can by spread by the data-flow type recovery analysis among other objects. 321 | \end{itemize} 322 | 323 | Refactoring actions are triggered either by hotkeys associated with them, or by pop-up menus shown on right-click. Actions are sensitive to the current context (current word under the cursor). As is shown in Figure~\ref{fig:context-1} and Figure~\ref{fig:context-2}, actions available for functions differ from actions for global variables. Available actions at any given position are composed of two sets of actions: 324 | \begin{itemize} 325 | \item Actions available for the current context, i.e. for functions, global variables, function calls, etc. This set may be empty. 326 | \item Global actions available at all posistions, i.e. navigation, current function comment modification, etc. 327 | \end{itemize} 328 | The complete catalog of available user actions is listed in Section~\ref{sec:all-interaction}. 329 | 330 | \begin{figure} 331 | \centering 332 | \begin{minipage}{.5\textwidth} 333 | \centering 334 | \includegraphics[width=7cm]{figures/context-actions-1-cut} 335 | \captionof{figure}{Context actions available for functions.} 336 | \label{fig:context-1} 337 | \end{minipage}% 338 | \begin{minipage}{.5\textwidth} 339 | \centering 340 | \includegraphics[width=7cm]{figures/context-actions-2-cut} 341 | \captionof{figure}{Context actions available for global variables.} 342 | \label{fig:context-2} 343 | \end{minipage} 344 | \end{figure} 345 | 346 | %----------------------------------------------------------------------------- 347 | % List of All User Actions 348 | %----------------------------------------------------------------------------- 349 | \section{List of All User Actions} 350 | \label{sec:all-interaction} 351 | This section provides a complete catalog of available user actions for all possible contexts. 352 | 353 | \subsection{Function-Declaration/Definition Context} 354 | \label{sec:context-fnc-def} 355 | Function actions are available on function declarations or definitions. They are listed in Table~\ref{table:function-context}. 356 | 357 | \begin{table}[!ht] 358 | \centering 359 | \caption{Function context user actions.} 360 | \label{table:function-context} 361 | \begin{tabular}{llc} 362 | \textbf{Action description} & \textbf{Hotkey} & \textbf{Triggers}\\ 363 | & & \textbf{re-decompilation}\\ 364 | \hline 365 | Jump to IDA's ASM & \texttt{A} & \xmark \\ 366 | Rename function & \texttt{N} & \xmark \\ 367 | Change type declaration & \texttt{Y} & \cmark \\ 368 | Open xrefs window & \texttt{X} & \xmark \\ 369 | Open calls window & \texttt{C} & \xmark \\ 370 | \end{tabular} 371 | \end{table} 372 | 373 | \subsection{Function-Call Context} 374 | Function-call actions are available on function calls. We can divide them into two categories: 375 | \begin{itemize} 376 | \item Calls of user defined functions---actions are the same as in function-declaration/definition context (Section~\ref{sec:context-fnc-def}), except the ``Change type declaration'' action. Also, you can double-click on a call to decompile/display the called function. 377 | \item Calls of dynamically linked functions---the only available action is double-click, which takes you on the import stub in the IDA's disassembly view. 378 | \end{itemize} 379 | 380 | \subsection{Global-Variable Context} 381 | Global-variable actions are available on global-variable definitions and uses. They are listed in Table~\ref{table:global-var-context}. 382 | 383 | \begin{table}[!ht] 384 | \centering 385 | \caption{Global-variable context user actions.} 386 | \label{table:global-var-context} 387 | \begin{tabular}{llc} 388 | \textbf{Action description} & \textbf{Hotkey} & \textbf{Triggers}\\ 389 | & & \textbf{re-decompilation}\\ 390 | \hline 391 | Jump to IDA's ASM & \texttt{A} & \xmark \\ 392 | Rename global variable & \texttt{N} & \xmark 393 | \end{tabular} 394 | \end{table} 395 | 396 | \subsection{Global Context} 397 | Global context actions are available everywhere. They are listed in Table~\ref{table:global-context}. 398 | 399 | \begin{table}[!ht] 400 | \centering 401 | \caption{Global context user actions.} 402 | \label{table:global-context} 403 | \begin{tabular}{llc} 404 | \textbf{Action description} & \textbf{Hotkey} & \textbf{Triggers}\\ 405 | & & \textbf{re-decompilation}\\ 406 | \hline 407 | Edit current function's comment & \texttt{;} & \xmark \\ 408 | Move backward (navigation) & \texttt{ESC} & \xmark \\ 409 | Move forward (navigation) & \texttt{CTRL+F} & \xmark 410 | \end{tabular} 411 | \end{table} 412 | 413 | %----------------------------------------------------------------------------- 414 | % Support an Feedback 415 | %----------------------------------------------------------------------------- 416 | \section{Support and Feedback} 417 | \label{sec:support} 418 | RetDec plugin is still in an experimental beta version. If you have any feedback, suggestions, or bug reports, please open an issue in the GitHub project (preferred), or send them to us either through our website, or through email. 419 | \begin{center} 420 | { 421 | \LARGE 422 | \href{https://github.com/avast/retdec-idaplugin}{https://github.com/avast/retdec-idaplugin} \\[5 mm] 423 | \href{https://retdec.com}{https://retdec.com} \\[5 mm] 424 | \href{mailto:support@retdec.com}{support@retdec.com} 425 | } 426 | \end{center} 427 | 428 | %----------------------------------------------------------------------------- 429 | % End of Document 430 | %----------------------------------------------------------------------------- 431 | \end{document} 432 | -------------------------------------------------------------------------------- /scripts/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(IDA_DIR) 2 | file(GLOB IDC_SCRIPTS "${CMAKE_CURRENT_SOURCE_DIR}/idc/*.idc") 3 | install(FILES ${IDC_SCRIPTS} DESTINATION "${IDA_DIR}/idc/") 4 | endif() 5 | -------------------------------------------------------------------------------- /scripts/idc/retdec-decompile-full.idc: -------------------------------------------------------------------------------- 1 | // 2 | // run by: 3 | // idal -A -S"retdec-decompile-full.idc /input.exe [--debug]" /input.exe 4 | // 5 | // output: 6 | // /input.exe.c 7 | // 8 | 9 | #include 10 | 11 | static main() 12 | { 13 | Message("[RD]\tWaiting for the end of the auto analysis...\n"); 14 | Wait(); 15 | 16 | if (ARGV.count != 2 && ARGV.count != 3) 17 | { 18 | Message("[RD]\tScript usage: retdec-decompile-full.idc /input.exe [--debug]\n"); 19 | Exit(1); 20 | } 21 | if (ARGV.count == 3) 22 | { 23 | if (ARGV[2] != "--debug") 24 | { 25 | Message("[RD]\tScript usage: retdec-decompile-full.idc /input.exe [--debug]\n"); 26 | Exit(1); 27 | } 28 | } 29 | 30 | auto in = ARGV[1]; 31 | SetInputFilePath(in); 32 | 33 | auto debug = (ARGV.count == 3); 34 | 35 | auto ret = 0; 36 | Message("[RD]\tRun Retargetable Decompiler...\n"); 37 | if (RunPlugin("retdec", 3)) 38 | { 39 | Message("[RD]\tOK: plugin run\n"); 40 | } 41 | else 42 | { 43 | Message("[RD]\tFAIL: plugin run\n"); 44 | ret = 1; 45 | } 46 | 47 | Message("[RD]\tAll done, exiting...\n"); 48 | 49 | if (debug) 50 | { 51 | Message("[RD]\tAll done, exit code = %d\n", ret); 52 | } 53 | else 54 | { 55 | Exit(ret); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /scripts/idc/retdec-decompile-selective.idc: -------------------------------------------------------------------------------- 1 | // 2 | // run by: 3 | // idal -A -S"retdec-decompile-selective.idc /input.exe
[--debug]" /input.exe 4 | // 5 | // output: 6 | // /input.exe.c 7 | // 8 | // note: 9 | // It is not possible to effectively change ASM position from this script. 10 | // Jump(ea) takes effect only after IDC script is finished. 11 | // Therefore, we can not use this simple mechanism (current position) to 12 | // select function to decompile. 13 | // We use hack to inform plugin which function to decompile: 14 | // Tag the function with "" comment -> run plugin -> reset 15 | // comment to original. 16 | // Invoked plugin option must find tagged function and decompile it. 17 | // 18 | 19 | #include 20 | 21 | static main() 22 | { 23 | Message("[RD]\tWaiting for the end of the auto analysis...\n"); 24 | Wait(); 25 | 26 | if (ARGV.count != 3&& ARGV.count != 4) 27 | { 28 | Message("[RD]\tScript usage: retdec-decompile-selective.idc /input.exe
[--debug]\n"); 29 | Exit(1); 30 | } 31 | if (ARGV.count == 4) 32 | { 33 | if (ARGV[3] != "--debug") 34 | { 35 | Message("[RD]\tScript usage: retdec-decompile-selective.idc /input.exe
[--debug]\n"); 36 | Exit(1); 37 | } 38 | } 39 | 40 | auto in = ARGV[1]; 41 | SetInputFilePath(in); 42 | 43 | auto ea = ARGV[2]; 44 | 45 | auto debug = (ARGV.count == 4); 46 | 47 | if (GetFunctionFlags(ea) == -1) 48 | { 49 | Message("[RD]\tFunction @ %a does NOT exist.\n", ea); 50 | if (!debug) 51 | { 52 | Exit(1); 53 | } 54 | } 55 | else 56 | { 57 | Message("[RD]\tFunction @ %a DOES exist.\n", ea); 58 | 59 | auto regular = 0; // non-repeatable 60 | auto cmt = GetFunctionCmt(ea, regular); 61 | SetFunctionCmt(ea, cmt+"", regular); 62 | 63 | auto ret = 0; 64 | Message("[RD]\tRun Retargetable Decompiler...\n"); 65 | if (RunPlugin("retdec", 2)) 66 | { 67 | Message("[RD]\tOK: plugin run\n"); 68 | } 69 | else 70 | { 71 | Message("[RD]\tFAIL: plugin run\n"); 72 | ret = 1; 73 | } 74 | 75 | SetFunctionCmt(ea, cmt, regular); 76 | 77 | Message("[RD]\tAll done, exiting...\n"); 78 | 79 | if (debug) 80 | { 81 | Message("[RD]\tAll done, exit code = %d\n", ret); 82 | } 83 | else 84 | { 85 | Exit(ret); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /scripts/run-ida-decompilation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """The script decompiles the given file via RetDec IDA plugin. 4 | The supported decompilation modes are: 5 | full - decompile entire input file. 6 | selective - decompile only the function selected by the given address. 7 | """ 8 | 9 | import argparse 10 | import os 11 | import shutil 12 | import signal 13 | import subprocess 14 | import sys 15 | 16 | 17 | script_full = 'retdec-decompile-full.idc' 18 | script_selective = 'retdec-decompile-selective.idc' 19 | 20 | TIMEOUT_RC = 137 21 | 22 | 23 | def is_windows(): 24 | return sys.platform in ('win32', 'msys') or os.name == 'nt' 25 | 26 | 27 | def print_error_and_die(*msg): 28 | print('Error:', *msg) 29 | sys.exit(1) 30 | 31 | 32 | def parse_args(args): 33 | parser = argparse.ArgumentParser(description=__doc__, 34 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 35 | 36 | parser.add_argument('file', 37 | metavar='FILE', 38 | help='The input file.') 39 | 40 | parser.add_argument('-o', '--output', 41 | dest='output', 42 | metavar='FILE', 43 | help='Output file (default: file.c). All but the last component must exist.') 44 | 45 | parser.add_argument('-i', '--ida', 46 | dest='ida_dir', 47 | default=os.environ.get('IDA_DIR'), 48 | help='Path to the IDA directory.') 49 | 50 | parser.add_argument('-d', '--idb', 51 | dest='idb_path', 52 | metavar='FILE', 53 | help='IDA DB file associated with input file.') 54 | 55 | parser.add_argument('-s', '--select', 56 | dest='selected_addr', 57 | help='Decompile only the function selected by the given address (any address inside function). Examples: 0x1000, 4096.') 58 | 59 | parser.add_argument('--ea64', 60 | dest='ea64', 61 | action='store_true', 62 | help='Use 64-bit address space plugin, i.e. retdec64 library and idat64 executable.') 63 | 64 | return parser.parse_args(args) 65 | 66 | 67 | def check_args(args): 68 | if args.ida_dir is None: 69 | print_error_and_die('Path to IDA directory was not specified.') 70 | if not os.path.isdir(args.ida_dir): 71 | print_error_and_die('Specified path to IDA directory is not a directory:', args.ida_dir) 72 | 73 | if args.ea64: 74 | args.idat_path = os.path.join(args.ida_dir, 'idat64.exe' if is_windows() else 'idat64') 75 | else: 76 | args.idat_path = os.path.join(args.ida_dir, 'idat.exe' if is_windows() else 'idat') 77 | 78 | if not os.path.exists(args.idat_path): 79 | print_error_and_die('IDA console application does not exist:', args.idat_path) 80 | 81 | if args.idb_path and not os.path.exists(args.idb_path): 82 | print_error_and_die('Specified IDB file does not exist:', args.idb_path) 83 | 84 | if not args.file or not os.path.exists(args.file): 85 | print_error_and_die('Specified input file does not exist:', args.file) 86 | args.file_dir = os.path.dirname(args.file) 87 | 88 | if not args.output: 89 | args.output = args.file + '.c' 90 | 91 | args.output_dir = os.path.dirname(args.output) 92 | if not os.path.exists(args.output_dir): 93 | print_error_and_die('Output directory does not exist:', args.output_dir) 94 | 95 | 96 | def main(): 97 | args = parse_args(sys.argv[1:]) 98 | check_args(args) 99 | 100 | if args.file_dir != args.output_dir: 101 | shutil.copy(args.file, args.output_dir) 102 | args.file = os.path.join(args.output_dir, os.path.basename(args.file)) 103 | ida_in = args.file 104 | if args.idb_path and os.path.dirname(args.idb_path) != args.output_dir: 105 | shutil.copy(args.idb_path, args.output_dir) 106 | args.idb_path = os.path.join(args.output_dir, os.path.basename(args.idb_path)) 107 | ida_in = args.idb_path 108 | 109 | rc = 0 110 | cmd = [args.idat_path, '-A'] 111 | 112 | # Select mode. 113 | if args.selected_addr: 114 | cmd.append('-S' + script_selective + ' "' + args.file + '" ' + args.selected_addr) 115 | # Full mode. 116 | else: 117 | cmd.append('-S' + script_full + ' "' + args.file + '"') 118 | 119 | #cmd.append(args.file) 120 | cmd.append(ida_in) 121 | 122 | print('RUN: ' + ' '.join(cmd)) 123 | rc = subprocess.call(cmd) 124 | 125 | # Plugin produces ".c" -> copy the file to the desired output. 126 | out = args.file + '.c' 127 | if os.path.exists(out) and out != args.output: 128 | shutil.copyfile(out, args.output) 129 | 130 | return rc 131 | 132 | 133 | if __name__ == "__main__": 134 | main() 135 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(idaplugin) 2 | -------------------------------------------------------------------------------- /src/idaplugin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## 2 | ## CMake build script for the IDA Pro plugin. 3 | ## 4 | 5 | # IDA SDK libs. 6 | if(WIN32) 7 | set(idasdk_ea32 "${IDA_SDK_DIR}/lib/x64_win_vc_32/ida.lib") 8 | set(idasdk_ea64 "${IDA_SDK_DIR}/lib/x64_win_vc_64/ida.lib") 9 | elseif(APPLE) 10 | set(idasdk_ea32 "${IDA_SDK_DIR}/lib/x64_mac_gcc_32/libida.dylib") 11 | set(idasdk_ea64 "${IDA_SDK_DIR}/lib/x64_mac_gcc_64/libida64.dylib") 12 | elseif(UNIX) # APPLE is also UNIX, so it MUST be before this elseif(). 13 | set(idasdk_ea32 "${IDA_SDK_DIR}/lib/x64_linux_gcc_32/libida.so") 14 | set(idasdk_ea64 "${IDA_SDK_DIR}/lib/x64_linux_gcc_64/libida64.so") 15 | else() 16 | message(FATAL_ERROR "Unsupported system type: ${CMAKE_SYSTEM_NAME}") 17 | endif() 18 | 19 | # Includes. 20 | include_directories("..") # Make our includes work. 21 | include_directories(SYSTEM 22 | "${IDA_SDK_DIR}/include" # Make IDA SDK includes work. 23 | ) 24 | 25 | # RetDec idaplugin sources. 26 | set(IDAPLUGIN_SOURCES 27 | config.cpp 28 | function.cpp 29 | place.cpp 30 | token.cpp 31 | retdec.cpp 32 | ui.cpp 33 | utils.cpp 34 | yx.cpp 35 | ) 36 | 37 | # RetDec idaplugin libs. 38 | add_library(idaplugin32 SHARED ${IDAPLUGIN_SOURCES}) 39 | add_library(idaplugin64 SHARED ${IDAPLUGIN_SOURCES}) 40 | 41 | target_compile_definitions(idaplugin64 PUBLIC __EA64__) 42 | 43 | target_link_libraries(idaplugin32 ${idasdk_ea32} retdec::retdec retdec::config retdec::utils retdec::deps::rapidjson) 44 | target_link_libraries(idaplugin64 ${idasdk_ea64} retdec::retdec retdec::config retdec::utils retdec::deps::rapidjson) 45 | 46 | if(MSYS) 47 | target_link_libraries(idaplugin32 ws2_32) 48 | target_link_libraries(idaplugin64 ws2_32) 49 | endif() 50 | 51 | # Due to the implementation of the plugin system in LLVM, we have to link our 52 | # libraries into retdec as a whole. 53 | if(MSVC) 54 | # -WHOLEARCHIVE needs path to the target, but when we use the target like 55 | # that, its properties (associated includes, etc.) are not propagated. 56 | # Therefore, we state 'bin2llvmir|llvmir2hll' twice in target_link_libraries(), 57 | # first as a target to get its properties, second as path to library to 58 | # link it as a whole. 59 | target_link_libraries(idaplugin32 60 | retdec::bin2llvmir -WHOLEARCHIVE:$ 61 | retdec::llvmir2hll -WHOLEARCHIVE:$ 62 | ) 63 | target_link_libraries(idaplugin64 64 | retdec::bin2llvmir -WHOLEARCHIVE:$ 65 | retdec::llvmir2hll -WHOLEARCHIVE:$ 66 | ) 67 | set_property(TARGET idaplugin32 68 | APPEND_STRING PROPERTY LINK_FLAGS " /FORCE:MULTIPLE" 69 | ) 70 | set_property(TARGET idaplugin64 71 | APPEND_STRING PROPERTY LINK_FLAGS " /FORCE:MULTIPLE" 72 | ) 73 | # Increase the stack size of the created binaries on MS Windows because the 74 | # default value is too small. The default Linux value is 8388608 (8 MB). 75 | set_property(TARGET idaplugin32 76 | APPEND_STRING PROPERTY LINK_FLAGS " /STACK:16777216" 77 | ) 78 | set_property(TARGET idaplugin64 79 | APPEND_STRING PROPERTY LINK_FLAGS " /STACK:16777216" 80 | ) 81 | # Allow the 32b version of bin2llvmir on Windows handle addresses larger 82 | # than 2 GB (up to 4 GB). 83 | if(CMAKE_SIZEOF_VOID_P MATCHES "4") 84 | set_property(TARGET idaplugin32 85 | APPEND_STRING PROPERTY LINK_FLAGS " /LARGEADDRESSAWARE" 86 | ) 87 | endif() 88 | if(CMAKE_SIZEOF_VOID_P MATCHES "4") 89 | set_property(TARGET idaplugin64 90 | APPEND_STRING PROPERTY LINK_FLAGS " /LARGEADDRESSAWARE" 91 | ) 92 | endif() 93 | elseif(APPLE) 94 | target_link_libraries(idaplugin32 95 | -Wl,-force_load retdec::bin2llvmir 96 | -Wl,-force_load retdec::llvmir2hll 97 | ) 98 | target_link_libraries(idaplugin64 99 | -Wl,-force_load retdec::bin2llvmir 100 | -Wl,-force_load retdec::llvmir2hll 101 | ) 102 | else() # Linux 103 | target_link_libraries(idaplugin32 104 | -Wl,--whole-archive retdec::bin2llvmir -Wl,--no-whole-archive 105 | -Wl,--whole-archive retdec::llvmir2hll -Wl,--no-whole-archive 106 | ) 107 | target_link_libraries(idaplugin64 108 | -Wl,--whole-archive retdec::bin2llvmir -Wl,--no-whole-archive 109 | -Wl,--whole-archive retdec::llvmir2hll -Wl,--no-whole-archive 110 | ) 111 | endif() 112 | 113 | set_target_properties(idaplugin32 PROPERTIES PREFIX "") 114 | set_target_properties(idaplugin64 PROPERTIES PREFIX "") 115 | set_target_properties(idaplugin32 PROPERTIES OUTPUT_NAME "retdec") 116 | set_target_properties(idaplugin64 PROPERTIES OUTPUT_NAME "retdec64") 117 | 118 | # Installation. 119 | if(IDA_DIR) 120 | install(TARGETS idaplugin32 idaplugin64 121 | LIBRARY DESTINATION "${IDA_DIR}/plugins/" 122 | RUNTIME DESTINATION "${IDA_DIR}/plugins/" 123 | ) 124 | install( 125 | FILES "decompiler-config.json" 126 | DESTINATION "${IDA_DIR}/plugins/retdec/" 127 | ) 128 | endif() 129 | -------------------------------------------------------------------------------- /src/idaplugin/config.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "config.h" 5 | #include "retdec.h" 6 | #include "utils.h" 7 | 8 | /** 9 | * Perform startup check that determines, if plugin can decompile IDA's input file. 10 | * @return True if plugin can decompile IDA's input, false otherwise. 11 | */ 12 | bool canDecompileInput( 13 | std::string& arch, 14 | std::string& endian, 15 | unsigned& bitSize, 16 | retdec::common::Address& rawSectionVma, 17 | retdec::common::Address& rawEntryPoint, 18 | bool& isRaw 19 | ) 20 | { 21 | std::string procName = inf_get_procname().c_str(); 22 | auto fileType = inf_get_filetype(); 23 | 24 | // 32-bit binary -> is_32bit() == 1 && is_64bit() == 0. 25 | // 64-bit binary -> is_32bit() == 1 && is_64bit() == 1. 26 | // Allow 64-bit x86 and arm. 27 | if (inf_is_64bit()) 28 | { 29 | if (!isX86() && procName != "ARM") 30 | { 31 | WARNING_GUI(RetDec::pluginName << " version " << RetDec::pluginVersion 32 | << " cannot decompile 64-bit for PROCNAME = " << procName 33 | ); 34 | return false; 35 | } 36 | } 37 | else if (!inf_is_32bit_exactly()) 38 | { 39 | WARNING_GUI(RetDec::pluginName << " version " << RetDec::pluginVersion 40 | << " cannot decompile PROCNAME = " << procName 41 | ); 42 | return false; 43 | } 44 | 45 | if (!(fileType == f_BIN 46 | || fileType == f_PE 47 | || fileType == f_ELF 48 | || fileType == f_COFF 49 | || fileType == f_MACHO 50 | || fileType == f_HEX)) 51 | { 52 | if (fileType == f_LOADER) 53 | { 54 | WARNING_GUI("Custom IDA loader plugin was used.\n" 55 | "Decompilation will be attempted, but:\n" 56 | "1. RetDec idaplugin can not check if the input can be " 57 | "decompiled. Decompilation may fail.\n" 58 | "2. If the custom loader behaves differently than the RetDec " 59 | "loader, decompilation may fail or produce nonsensical result." 60 | ); 61 | } 62 | else 63 | { 64 | WARNING_GUI(RetDec::pluginName 65 | << " version " << RetDec::pluginVersion 66 | << " cannot decompile this input file (file type = " 67 | << fileType << ").\n" 68 | ); 69 | return false; 70 | } 71 | } 72 | 73 | // Check Intel HEX. 74 | // 75 | if (fileType == f_HEX) 76 | { 77 | if (procName == "mipsr" || procName == "mipsb") 78 | { 79 | arch = "mips"; 80 | endian = "big"; 81 | } 82 | else if (procName == "mipsrl" 83 | || procName == "mipsl" 84 | || procName == "psp") 85 | { 86 | arch = "mips"; 87 | endian = "little"; 88 | } 89 | else 90 | { 91 | WARNING_GUI("Intel HEX input file can be decompiled only for one of " 92 | "these {mipsr, mipsb, mipsrl, mipsl, psp} processors, " 93 | "not \"" << procName << "\".\n"); 94 | return false; 95 | } 96 | } 97 | 98 | // Check BIN (RAW). 99 | // 100 | if (fileType == f_BIN) 101 | { 102 | if (inf_is_64bit) bitSize = 64; 103 | else if (inf_is_32bit_exactly) bitSize = 32; 104 | else 105 | { 106 | WARNING_GUI("Can decompile only 32/64 bit f_BIN.\n"); 107 | return false; 108 | } 109 | isRaw = true; 110 | 111 | // Section VMA. 112 | // 113 | rawSectionVma = inf_get_min_ea(); 114 | 115 | // Entry point. 116 | // 117 | if (inf_get_start_ea() != BADADDR) 118 | { 119 | rawEntryPoint = inf_get_start_ea(); 120 | } 121 | else 122 | { 123 | rawEntryPoint = rawSectionVma; 124 | } 125 | 126 | // Architecture + endian. 127 | // 128 | if (procName == "mipsr" || procName == "mipsb") 129 | { 130 | arch = "mips"; 131 | endian = "big"; 132 | } 133 | else if (procName == "mipsrl" || procName == "mipsl" || procName == "psp") 134 | { 135 | arch = "mips"; 136 | endian = "little"; 137 | } 138 | else if (procName == "ARM") 139 | { 140 | arch = "arm"; 141 | endian = "little"; 142 | } 143 | else if (procName == "ARMB") 144 | { 145 | arch = "arm"; 146 | endian = "big"; 147 | } 148 | else if (procName == "PPCL") 149 | { 150 | arch = "powerpc"; 151 | endian = "little"; 152 | } 153 | else if (procName == "PPC") 154 | { 155 | arch = "powerpc"; 156 | endian = "big"; 157 | } 158 | else if (isX86()) 159 | { 160 | arch = inf_is_64bit() ? "x86-64" : "x86"; 161 | endian = "little"; 162 | } 163 | else 164 | { 165 | WARNING_GUI("Binary input file can be decompiled only for one of these " 166 | "{mipsr, mipsb, mipsrl, mipsl, psp, ARM, ARMB, PPCL, PPC, 80386p, " 167 | "80386r, 80486p, 80486r, 80586p, 80586r, 80686p, p2, p3, p4} " 168 | "processors, not \"" << procName << "\".\n"); 169 | return false; 170 | } 171 | } 172 | 173 | return true; 174 | } 175 | 176 | bool generateHeader(retdec::config::Config& config, std::string out) 177 | { 178 | auto inFile = getInputPath(); 179 | if (inFile.empty()) 180 | { 181 | WARNING_GUI("Cannot decompile - there is no input file."); 182 | return true; 183 | } 184 | 185 | std::string arch; 186 | std::string endian; 187 | unsigned bitSize = 0; 188 | retdec::common::Address rawSectionVma; 189 | retdec::common::Address rawEntryPoint; 190 | bool isRaw = false; 191 | if (!canDecompileInput( 192 | arch, 193 | endian, 194 | bitSize, 195 | rawSectionVma, 196 | rawEntryPoint, 197 | isRaw)) 198 | { 199 | return true; 200 | } 201 | 202 | auto idaPath = retdec::utils::getThisBinaryDirectoryPath(); 203 | auto configPath = idaPath; 204 | configPath.append("plugins"); 205 | configPath.append("retdec"); 206 | configPath.append("decompiler-config.json"); 207 | if (fs::exists(configPath)) 208 | { 209 | config = retdec::config::Config::fromFile(configPath.string()); 210 | config.parameters.fixRelativePaths(idaPath.string()); 211 | } 212 | 213 | if (!arch.empty()) 214 | { 215 | config.architecture.setName(arch); 216 | } 217 | if (endian == "little") 218 | { 219 | config.architecture.setIsEndianLittle(); 220 | } 221 | else if (endian == "big") 222 | { 223 | config.architecture.setIsEndianBig(); 224 | } 225 | if (rawSectionVma.isDefined()) 226 | { 227 | config.parameters.setSectionVMA(rawSectionVma); 228 | } 229 | if (rawEntryPoint.isDefined()) 230 | { 231 | config.parameters.setEntryPoint(rawEntryPoint); 232 | } 233 | 234 | if (isRaw && bitSize) 235 | { 236 | config.fileFormat.setIsRaw(); 237 | config.fileFormat.setFileClassBits(bitSize); 238 | config.architecture.setBitSize(bitSize); 239 | } 240 | 241 | config.parameters.setInputFile(inFile); 242 | config.parameters.setOutputFile(out); 243 | 244 | return false; 245 | } 246 | 247 | std::string defaultTypeString() 248 | { 249 | return "i32"; 250 | } 251 | 252 | /** 253 | * TODO - recursive structure types? 254 | */ 255 | std::string type2string( 256 | retdec::config::Config& config, 257 | std::map& structIdSet, 258 | const tinfo_t &type) 259 | { 260 | std::string ret = defaultTypeString(); 261 | 262 | if (type.empty()) 263 | return ret; 264 | 265 | if (type.is_char() || type.is_uchar()) ret = "i8"; 266 | else if (type.is_int16() || type.is_uint16()) ret = "i16"; 267 | else if (type.is_int32() || type.is_uint() || type.is_uint32()) ret = "i32"; 268 | else if (type.is_int64() || type.is_uint64()) ret = "i64"; 269 | else if (type.is_int128()) ret = "i128"; 270 | else if (type.is_ldouble()) ret = "f80"; 271 | else if (type.is_double()) ret = "double"; 272 | else if (type.is_float()) ret = "float"; 273 | else if (type.is_bool()) ret = "i1"; 274 | else if (type.is_void()) ret = "void"; 275 | else if (type.is_unknown()) ret = "i32"; 276 | 277 | else if (type.is_ptr()) 278 | { 279 | tinfo_t base = type.get_pointed_object(); 280 | ret = type2string(config, structIdSet, base) + "*"; 281 | } 282 | else if (type.is_func()) 283 | { 284 | func_type_data_t fncType; 285 | if (type.get_func_details(&fncType)) 286 | { 287 | ret = type2string(config, structIdSet, fncType.rettype); 288 | ret += "("; 289 | 290 | bool first = true; 291 | for (auto const &a : fncType) 292 | { 293 | if (first) 294 | { 295 | first = false; 296 | } 297 | else 298 | { 299 | ret += ", "; 300 | } 301 | 302 | ret += type2string(config, structIdSet, a.type); 303 | } 304 | 305 | ret += ")"; 306 | } 307 | else 308 | { 309 | ret = "i32*"; 310 | } 311 | } 312 | else if (type.is_array()) 313 | { 314 | tinfo_t base = type.get_array_element(); 315 | std::string baseType = type2string(config, structIdSet, base); 316 | int arraySize = type.get_array_nelems(); 317 | 318 | if (arraySize > 0) 319 | { 320 | ret = "[" + std::to_string(arraySize) + " x " + baseType + "]"; 321 | } 322 | else 323 | { 324 | ret = baseType + "*"; 325 | } 326 | } 327 | else if (type.is_struct()) 328 | { 329 | auto it = structIdSet.find(type); 330 | std::string strName = "%"; 331 | 332 | // This structure have already been generated. 333 | // 334 | if (it != structIdSet.end()) 335 | { 336 | return it->second; 337 | } 338 | else 339 | { 340 | qstring idaStrName = ""; // make sure it is empty. 341 | 342 | if (type.get_final_type_name(&idaStrName) && !idaStrName.empty()) 343 | { 344 | strName += idaStrName.c_str(); 345 | } 346 | else 347 | { 348 | strName += "struct_" + std::to_string(config.structures.size()); 349 | } 350 | 351 | structIdSet[type] = strName; 352 | } 353 | 354 | std::string body; 355 | 356 | int elemCnt = type.get_udt_nmembers(); 357 | if (elemCnt > 0) 358 | { 359 | body = "{ "; 360 | 361 | bool first = true; 362 | for (int i=0; i= 0) 369 | { 370 | memType = type2string(config, structIdSet, mem.type); 371 | } 372 | 373 | if (first) 374 | { 375 | first = false; 376 | } 377 | else 378 | { 379 | body += ", "; 380 | } 381 | 382 | body += memType; 383 | } 384 | 385 | body += " }"; 386 | } 387 | else 388 | { 389 | body = "{ " + defaultTypeString() + " }"; 390 | } 391 | 392 | ret = strName; // only structure name is returned. 393 | 394 | retdec::common::Type ccType(strName + " = type " + body); 395 | config.structures.insert(ccType); 396 | } 397 | else if (type.is_union()) 398 | { 399 | ret = defaultTypeString(); 400 | } 401 | else if (type.is_enum()) 402 | { 403 | ret = defaultTypeString(); 404 | } 405 | else if (type.is_sue()) 406 | { 407 | ret = defaultTypeString(); 408 | } 409 | // http://en.cppreference.com/w/cpp/language/bit_field 410 | else if (type.is_bitfield()) 411 | { 412 | ret = defaultTypeString(); 413 | } 414 | else 415 | { 416 | ret = defaultTypeString(); 417 | } 418 | 419 | return ret; 420 | } 421 | 422 | std::string addrType2string(ea_t addr) 423 | { 424 | flags_t f = get_full_flags(addr); 425 | if (f == 0) 426 | { 427 | return defaultTypeString(); 428 | } 429 | 430 | asize_t itemSize = get_item_size(addr); 431 | asize_t elemSize = get_data_elsize(addr, f); 432 | asize_t arraySize = 0; 433 | if (itemSize > elemSize) 434 | { 435 | arraySize = itemSize / elemSize; 436 | } 437 | 438 | std::string item = defaultTypeString(); 439 | if (is_byte(f)) 440 | { 441 | item = "i8"; 442 | } 443 | else if (is_word(f)) 444 | { 445 | item = "i16"; 446 | } 447 | else if (is_dword(f)) 448 | { 449 | item = "i32"; 450 | } 451 | else if (is_qword(f)) 452 | { 453 | item = "i64"; 454 | } 455 | else if (is_oword(f)) 456 | { 457 | item = "i128"; 458 | } 459 | else if (is_yword(f)) 460 | { 461 | item = "i256"; 462 | } 463 | else if (is_tbyte(f)) 464 | { 465 | item = "i80"; 466 | } 467 | else if (is_float(f)) 468 | { 469 | item = "float"; 470 | } 471 | else if (is_double(f)) 472 | { 473 | item = "double"; 474 | } 475 | else if (is_pack_real(f)) 476 | { 477 | item = "x86_fp80"; // TODO: ??? maybe 12B = 96b. 478 | } 479 | else if (is_strlit(f)) 480 | { 481 | item = "i8"; 482 | } 483 | else if (is_struct(f)) 484 | { 485 | item = defaultTypeString(); // TODO: not supported right now. 486 | } 487 | else if (is_align(f)) 488 | { 489 | item = "i" + std::to_string(elemSize); 490 | } 491 | else if (is_custom(f)) 492 | { 493 | item = defaultTypeString(); // TODO: not supported right now. 494 | } 495 | else 496 | { 497 | item = defaultTypeString(); 498 | } 499 | 500 | std::string ret = defaultTypeString(); 501 | if (arraySize) 502 | { 503 | ret = "[" + std::to_string(arraySize) + " x " + item + "]"; 504 | } 505 | else 506 | { 507 | ret = item; 508 | } 509 | return ret; 510 | } 511 | 512 | bool isLinkedFunction(func_t* fnc) 513 | { 514 | // Either there is no code in function = no instructions, 515 | // or only instructions have "retn" mnemonics. 516 | // 517 | for (ea_t addr = fnc->start_ea; addr < fnc->end_ea; ++addr) 518 | { 519 | flags_t flags = get_flags(addr); 520 | if (is_code(flags)) 521 | { 522 | qstring mnem; 523 | print_insn_mnem(&mnem, addr); 524 | if (mnem != "retn") 525 | { 526 | return false; 527 | } 528 | } 529 | } 530 | 531 | return true; 532 | } 533 | 534 | void generateCallingConvention( 535 | const cm_t &idaCC, 536 | retdec::common::CallingConvention &configCC) 537 | { 538 | switch (idaCC) 539 | { 540 | case CM_CC_VOIDARG: configCC.setIsVoidarg(); break; 541 | case CM_CC_CDECL: configCC.setIsCdecl(); break; 542 | case CM_CC_ELLIPSIS: configCC.setIsCdecl(); break; 543 | case CM_CC_STDCALL: configCC.setIsStdcall(); break; 544 | case CM_CC_PASCAL: configCC.setIsPascal(); break; 545 | case CM_CC_FASTCALL: configCC.setIsFastcall(); break; 546 | case CM_CC_THISCALL: configCC.setIsThiscall(); break; 547 | case CM_CC_MANUAL: configCC.setIsManual(); break; 548 | case CM_CC_SPOILED: configCC.setIsSpoiled(); break; 549 | case CM_CC_SPECIALE: configCC.setIsSpecialE(); break; 550 | case CM_CC_SPECIALP: configCC.setIsSpecialP(); break; 551 | case CM_CC_SPECIAL: configCC.setIsSpecial(); break; 552 | 553 | case CM_CC_INVALID: 554 | case CM_CC_UNKNOWN: 555 | case CM_CC_GOLANG: 556 | case CM_CC_RESERVE3: 557 | default: configCC.setIsUnknown(); break; 558 | } 559 | } 560 | 561 | retdec::common::Storage generateObjectLocation( 562 | const argloc_t &loc, 563 | const tinfo_t &locType) 564 | { 565 | if (loc.is_reg()) // is_reg1() || is_reg2() 566 | { 567 | qstring buff; 568 | if (get_reg_name(&buff, loc.reg1(), locType.get_size()) <= 0) 569 | { 570 | return retdec::common::Storage::undefined(); 571 | } 572 | 573 | return retdec::common::Storage::inRegister(buff.c_str()); 574 | } 575 | else if (loc.is_stkoff()) 576 | { 577 | return retdec::common::Storage::onStack(loc.stkoff()); 578 | } 579 | else if (loc.is_ea()) 580 | { 581 | return retdec::common::Storage::inMemory(loc.get_ea()); 582 | } 583 | else if (loc.is_rrel()) 584 | { 585 | return retdec::common::Storage::undefined(); 586 | } 587 | else if (loc.is_scattered()) 588 | { 589 | return retdec::common::Storage::undefined(); 590 | } 591 | else if (loc.is_fragmented()) 592 | { 593 | return retdec::common::Storage::undefined(); 594 | } 595 | else if (loc.is_custom()) 596 | { 597 | return retdec::common::Storage::undefined(); 598 | } 599 | else if (loc.is_badloc()) 600 | { 601 | return retdec::common::Storage::undefined(); 602 | } 603 | else 604 | { 605 | return retdec::common::Storage::undefined(); 606 | } 607 | } 608 | 609 | void generateFunctionType( 610 | retdec::config::Config& config, 611 | std::map& structIdSet, 612 | const tinfo_t &fncType, 613 | retdec::common::Function &ccFnc) 614 | { 615 | // Generate arguments and return from function type. 616 | // 617 | func_type_data_t fncInfo; 618 | if (fncType.get_func_details(&fncInfo)) 619 | { 620 | // Return info. 621 | // 622 | ccFnc.returnType.setLlvmIr(type2string( 623 | config, 624 | structIdSet, 625 | fncInfo.rettype) 626 | ); 627 | ccFnc.returnStorage = generateObjectLocation( 628 | fncInfo.retloc, 629 | fncInfo.rettype 630 | ); 631 | 632 | // Argument info. 633 | // 634 | unsigned cntr = 1; 635 | for (auto const& a : fncInfo) 636 | { 637 | std::string name = a.name.c_str(); 638 | if (name.empty()) 639 | { 640 | name = "a" + std::to_string(cntr); 641 | } 642 | 643 | auto s = generateObjectLocation(a.argloc, a.type); 644 | retdec::common::Object arg(name, s); 645 | arg.type.setLlvmIr(type2string(config, structIdSet, a.type)); 646 | 647 | ccFnc.parameters.push_back(arg); 648 | 649 | ++cntr; 650 | } 651 | 652 | // Calling convention. 653 | // 654 | generateCallingConvention(fncType.get_cc(), ccFnc.callingConvention); 655 | if (fncType.get_cc() == CM_CC_ELLIPSIS) 656 | { 657 | ccFnc.setIsVariadic(true); 658 | } 659 | } 660 | else 661 | { 662 | // TODO: ??? 663 | } 664 | } 665 | 666 | void generateFunction( 667 | retdec::config::Config& config, 668 | std::map& structIdSet, 669 | func_t* fnc) 670 | { 671 | qstring qFncName; 672 | get_func_name(&qFncName, fnc->start_ea); 673 | 674 | std::string fncName = qFncName.c_str(); 675 | std::replace(fncName.begin(), fncName.end(), '.', '_'); 676 | 677 | retdec::common::Function ccFnc(fncName); 678 | ccFnc.setStart(fnc->start_ea); 679 | ccFnc.setEnd(fnc->end_ea); 680 | // TODO: return type is always set to default: ugly, make it better. 681 | ccFnc.returnType.setLlvmIr(defaultTypeString()); 682 | 683 | qstring qCmt; 684 | if (get_func_cmt(&qCmt, fnc, false) > 0) 685 | { 686 | ccFnc.setComment(qCmt.c_str()); 687 | } 688 | 689 | qstring qDemangled; 690 | if (demangle_name(&qDemangled, fncName.c_str(), MNG_SHORT_FORM) > 0) 691 | { 692 | ccFnc.setDemangledName(qDemangled.c_str()); 693 | } 694 | 695 | if (fnc->flags & FUNC_STATICDEF) 696 | { 697 | ccFnc.setIsStaticallyLinked(); 698 | } 699 | else if (fnc->flags & FUNC_LIB) 700 | { 701 | ccFnc.setIsDynamicallyLinked(); 702 | } 703 | else if (isLinkedFunction(fnc)) 704 | { 705 | ccFnc.setIsDynamicallyLinked(); 706 | } 707 | else 708 | { 709 | ccFnc.setIsUserDefined(); 710 | } 711 | 712 | // For IDA 6.x (don't know about IDA 7.x): 713 | // get_tinfo2() is preferred before guess_func_tinfo2() 714 | // for unknown reason, guess_func_tinfo2() sometimes mix up the 715 | // arguments (vawtrak sub_10021A76). 716 | // 717 | tinfo_t fncType; 718 | get_tinfo(&fncType, fnc->start_ea); 719 | if (!fncType.is_func()) 720 | { 721 | // Guess type from first instruction address. 722 | // 723 | if (guess_tinfo(&fncType, fnc->start_ea) != GUESS_FUNC_OK) 724 | { 725 | // problem 726 | } 727 | } 728 | 729 | if (fncType.is_func()) 730 | { 731 | generateFunctionType(config, structIdSet, fncType, ccFnc); 732 | } 733 | 734 | config.functions.insert(ccFnc); 735 | } 736 | 737 | void generateFunctions( 738 | retdec::config::Config& config, 739 | std::map& structIdSet) 740 | { 741 | for (unsigned i = 0; i < get_func_qty(); ++i) 742 | { 743 | generateFunction(config, structIdSet, getn_func(i)); 744 | } 745 | } 746 | 747 | void generateGlobals( 748 | retdec::config::Config& config, 749 | std::map& structIdSet) 750 | { 751 | qstring buff; 752 | 753 | int segNum = get_segm_qty(); 754 | for (int i = 0; i < segNum; ++i) 755 | { 756 | segment_t* seg = getnseg(i); 757 | if (seg == nullptr) 758 | { 759 | continue; 760 | } 761 | 762 | if (get_visible_segm_name(&buff, seg) <= 0) 763 | { 764 | continue; 765 | } 766 | 767 | ea_t head = seg->start_ea - 1; 768 | while ( (head = next_head(head, seg->end_ea)) != BADADDR) 769 | { 770 | flags_t f = get_full_flags(head); 771 | if (f == 0) 772 | { 773 | continue; 774 | } 775 | 776 | // Argument 1 should not be present for data. 777 | // Some object do have argument 0 (off_X), some dont (strings). 778 | // 779 | if (!is_data(f) || !is_head(f) || /*!is_defarg0(f) ||*/ is_defarg1(f)) 780 | { 781 | continue; 782 | } 783 | 784 | if (!has_any_name(f)) // usually alignment. 785 | { 786 | continue; 787 | } 788 | 789 | if (get_name(&buff, head) <= 0) 790 | { 791 | continue; 792 | } 793 | 794 | auto s = retdec::common::Storage::inMemory( 795 | retdec::common::Address(head)); 796 | retdec::common::Object global(buff.c_str(), s); 797 | 798 | // Get type. 799 | // 800 | tinfo_t getType; 801 | get_tinfo(&getType, head); 802 | 803 | if (!getType.empty() && getType.present() && getType.is_func()) 804 | { 805 | if (config.functions.getFunctionByStartAddress(head) != nullptr) 806 | { 807 | continue; 808 | } 809 | 810 | std::string fncName = buff.c_str(); 811 | std::replace(fncName.begin(), fncName.end(), '.', '_'); 812 | 813 | retdec::common::Function ccFnc(fncName); 814 | ccFnc.setStart(head); 815 | ccFnc.setEnd(head); 816 | ccFnc.setIsDynamicallyLinked(); 817 | generateFunctionType(config, structIdSet, getType, ccFnc); 818 | 819 | qstring qDemangled; 820 | if (demangle_name(&qDemangled, fncName.c_str(), MNG_SHORT_FORM) > 0) 821 | { 822 | ccFnc.setDemangledName(qDemangled.c_str()); 823 | } 824 | 825 | config.functions.insert(ccFnc); 826 | continue; 827 | } 828 | 829 | // Continue creating global variable. 830 | // 831 | if (!getType.empty() && getType.present()) 832 | { 833 | global.type.setLlvmIr(type2string(config, structIdSet, getType)); 834 | } 835 | else 836 | { 837 | global.type.setLlvmIr(addrType2string(head)); 838 | } 839 | 840 | config.globals.insert(global); 841 | } 842 | } 843 | } 844 | 845 | bool fillConfig(retdec::config::Config& config, const std::string& out) 846 | { 847 | std::map structIdSet; 848 | 849 | config.structures.clear(); 850 | config.functions.clear(); 851 | config.globals.clear(); 852 | 853 | if (generateHeader(config, out)) 854 | { 855 | return true; 856 | } 857 | generateFunctions(config, structIdSet); 858 | generateGlobals(config, structIdSet); 859 | 860 | return false; 861 | } 862 | -------------------------------------------------------------------------------- /src/idaplugin/config.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETDEC_CONFIG_H 3 | #define RETDEC_CONFIG_H 4 | 5 | #include 6 | 7 | /** 8 | * Returns \c true if something went wrong. 9 | */ 10 | bool fillConfig(retdec::config::Config& config, const std::string& out = ""); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/idaplugin/decompiler-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "decompParams": { 3 | "verboseOut": false, 4 | "outputFormat": "json", 5 | "keepAllFuncs": true, 6 | "selectedDecodeOnly": false, 7 | "detectStaticCode": false, 8 | "backendDisabledOpts": "", 9 | "backendEnabledOpts": "", 10 | "backendCallInfoObtainer": "optim", 11 | "backendVarRenamer": "readable", 12 | "backendNoOpts": false, 13 | "backendEmitCfg": false, 14 | "backendEmitCg": false, 15 | "backendAggressiveOpts": false, 16 | "backendKeepAllBrackets": false, 17 | "backendKeepLibraryFuncs": false, 18 | "backendNoTimeVaryingInfo": false, 19 | "backendNoVarRenaming": false, 20 | "backendNoCompoundOperators": false, 21 | "backendNoSymbolicNames": false, 22 | "timeout": 0, 23 | "maxMemoryLimit": 0, 24 | "maxMemoryLimitHalfRam": true, 25 | "ordinalNumDirectory": "./plugins/retdec/ordinals/", 26 | "staticSignPaths": [], 27 | "libraryTypeInfoPaths": [ 28 | "./plugins/retdec/types/arm.json", 29 | "./plugins/retdec/types/cstdlib.json", 30 | "./plugins/retdec/types/linux.json", 31 | "./plugins/retdec/types/windows.json", 32 | "./plugins/retdec/types/windrivers.json" 33 | ], 34 | "cryptoPatternPaths": [ 35 | "./plugins/retdec/yara_patterns/signsrch/signsrch.yarac", 36 | "./plugins/retdec/yara_patterns/signsrch/signsrch_regex.yarac" 37 | ], 38 | "llvmPasses" : [ 39 | "retdec-provider-init", 40 | "retdec-decoder", 41 | "verify", 42 | "retdec-x86-addr-spaces", 43 | "retdec-x87-fpu", 44 | "retdec-main-detection", 45 | "retdec-idioms-libgcc", 46 | "retdec-inst-opt", 47 | "retdec-cond-branch-opt", 48 | "retdec-syscalls", 49 | "retdec-stack", 50 | "retdec-constants", 51 | "retdec-param-return", 52 | "retdec-inst-opt-rda", 53 | "retdec-inst-opt", 54 | "retdec-simple-types", 55 | "retdec-remove-asm-instrs", 56 | "retdec-class-hierarchy", 57 | "retdec-select-fncs", 58 | "retdec-unreachable-funcs", 59 | "retdec-inst-opt", 60 | "retdec-register-localization", 61 | "retdec-value-protect", 62 | "instcombine", 63 | "tbaa", 64 | "basicaa", 65 | "simplifycfg", 66 | "early-cse", 67 | "tbaa", 68 | "basicaa", 69 | "globalopt", 70 | "mem2reg", 71 | "instcombine", 72 | "simplifycfg", 73 | "early-cse", 74 | "lazy-value-info", 75 | "jump-threading", 76 | "correlated-propagation", 77 | "simplifycfg", 78 | "instcombine", 79 | "simplifycfg", 80 | "reassociate", 81 | "loops", 82 | "loop-simplify", 83 | "lcssa", 84 | "loop-rotate", 85 | "licm", 86 | "lcssa", 87 | "instcombine", 88 | "loop-simplifycfg", 89 | "loop-simplify", 90 | "aa", 91 | "loop-accesses", 92 | "loop-load-elim", 93 | "lcssa", 94 | "indvars", 95 | "loop-idiom", 96 | "loop-deletion", 97 | "gvn", 98 | "sccp", 99 | "instcombine", 100 | "lazy-value-info", 101 | "jump-threading", 102 | "correlated-propagation", 103 | "dse", 104 | "bdce", 105 | "adce", 106 | "simplifycfg", 107 | "instcombine", 108 | "strip-dead-prototypes", 109 | "globaldce", 110 | "constmerge", 111 | "constprop", 112 | "instcombine", 113 | "instcombine", 114 | "tbaa", 115 | "basicaa", 116 | "simplifycfg", 117 | "early-cse", 118 | "tbaa", 119 | "basicaa", 120 | "globalopt", 121 | "mem2reg", 122 | "instcombine", 123 | "simplifycfg", 124 | "early-cse", 125 | "lazy-value-info", 126 | "jump-threading", 127 | "correlated-propagation", 128 | "simplifycfg", 129 | "instcombine", 130 | "simplifycfg", 131 | "reassociate", 132 | "loops", 133 | "loop-simplify", 134 | "lcssa", 135 | "loop-rotate", 136 | "licm", 137 | "lcssa", 138 | "instcombine", 139 | "loop-simplifycfg", 140 | "loop-simplify", 141 | "aa", 142 | "loop-accesses", 143 | "loop-load-elim", 144 | "lcssa", 145 | "indvars", 146 | "loop-idiom", 147 | "loop-deletion", 148 | "gvn", 149 | "sccp", 150 | "instcombine", 151 | "lazy-value-info", 152 | "jump-threading", 153 | "correlated-propagation", 154 | "dse", 155 | "bdce", 156 | "adce", 157 | "simplifycfg", 158 | "instcombine", 159 | "strip-dead-prototypes", 160 | "globaldce", 161 | "constmerge", 162 | "constprop", 163 | "instcombine", 164 | "retdec-inst-opt", 165 | "retdec-simple-types", 166 | "retdec-stack-ptr-op-remove", 167 | "retdec-idioms", 168 | "instcombine", 169 | "retdec-inst-opt", 170 | "retdec-idioms", 171 | "retdec-remove-phi", 172 | "sink", 173 | "verify", 174 | "loops", 175 | "scalar-evolution", 176 | "retdec-value-protect", 177 | "retdec-llvmir2hll" 178 | ] 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/idaplugin/function.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "function.h" 5 | 6 | Function::Function() 7 | { 8 | 9 | } 10 | 11 | Function::Function(func_t* f, const std::vector& tokens) 12 | : _fnc(f) 13 | { 14 | std::size_t y = YX::starting_y; 15 | std::size_t x = YX::starting_x; 16 | for (auto& t : tokens) 17 | { 18 | _tokens[YX(y, x)] = t; 19 | 20 | if (_ea2yx.count(t.ea) == 0) 21 | { 22 | _ea2yx[t.ea] = YX(y, x); 23 | } 24 | 25 | if (t.kind == Token::Kind::NEW_LINE) 26 | { 27 | ++y; 28 | x = YX::starting_x; 29 | } 30 | else 31 | { 32 | x += t.value.size(); 33 | } 34 | } 35 | } 36 | 37 | func_t* Function::fnc() const 38 | { 39 | return _fnc; 40 | } 41 | 42 | std::string Function::getName() const 43 | { 44 | qstring qFncName; 45 | get_func_name(&qFncName, fnc()->start_ea); 46 | return qFncName.c_str(); 47 | } 48 | 49 | ea_t Function::getStart() const 50 | { 51 | return _fnc->start_ea; 52 | } 53 | 54 | ea_t Function::getEnd() const 55 | { 56 | return _fnc->end_ea; 57 | } 58 | 59 | const Token* Function::getToken(YX yx) const 60 | { 61 | auto it = _tokens.find(adjust_yx(yx)); 62 | return it == _tokens.end() ? nullptr : &it->second; 63 | } 64 | 65 | const std::map& Function::getTokens() const 66 | { 67 | return _tokens; 68 | } 69 | 70 | YX Function::min_yx() const 71 | { 72 | return _tokens.empty() ? YX::starting_yx : _tokens.begin()->first; 73 | } 74 | 75 | YX Function::max_yx() const 76 | { 77 | return _tokens.empty() ? YX::starting_yx : _tokens.rbegin()->first; 78 | } 79 | 80 | YX Function::prev_yx(YX yx) const 81 | { 82 | auto it = _tokens.find(adjust_yx(yx)); 83 | if (it == _tokens.end() || it == _tokens.begin()) 84 | { 85 | return yx; 86 | } 87 | --it; 88 | return it->first; 89 | } 90 | 91 | YX Function::next_yx(YX yx) const 92 | { 93 | auto it = _tokens.find(adjust_yx(yx)); 94 | auto nit = it; 95 | ++nit; 96 | if (it == _tokens.end() || nit == _tokens.end()) 97 | { 98 | return yx; 99 | } 100 | return nit->first; 101 | } 102 | 103 | YX Function::adjust_yx(YX yx) const 104 | { 105 | if (_tokens.empty() || _tokens.count(yx)) 106 | { 107 | return yx; 108 | } 109 | if (yx <= min_yx()) 110 | { 111 | return min_yx(); 112 | } 113 | if (yx >= max_yx()) 114 | { 115 | return max_yx(); 116 | } 117 | 118 | auto it = _tokens.upper_bound(yx); 119 | --it; 120 | return it->first; 121 | } 122 | 123 | std::string Function::line_yx(YX yx) const 124 | { 125 | std::string line; 126 | 127 | auto it = _tokens.find(adjust_yx(yx)); 128 | while (it != _tokens.end() 129 | && it->first.y == yx.y 130 | && it->second.kind != Token::Kind::NEW_LINE) 131 | { 132 | line += std::string(SCOLOR_ON) 133 | + it->second.getColorTag() 134 | + it->second.value 135 | + SCOLOR_OFF 136 | + it->second.getColorTag(); 137 | ++it; 138 | } 139 | 140 | return line; 141 | } 142 | 143 | ea_t Function::yx_2_ea(YX yx) const 144 | { 145 | auto it = _tokens.find(adjust_yx(yx)); 146 | if (it == _tokens.end()) 147 | { 148 | return BADADDR; 149 | } 150 | return it->second.ea; 151 | } 152 | 153 | std::set Function::yx_2_eas(YX yx) const 154 | { 155 | std::set ret; 156 | auto it = _tokens.find(YX(yx.y, 0)); 157 | while (it != _tokens.end() && it->first.y == yx.y) 158 | { 159 | ret.insert(it->second.ea); 160 | ++it; 161 | } 162 | return ret; 163 | } 164 | 165 | YX Function::ea_2_yx(ea_t ea) const 166 | { 167 | if (_ea2yx.empty()) 168 | { 169 | return YX::starting_yx; 170 | } 171 | if (ea < _ea2yx.begin()->first || _ea2yx.rbegin()->first < ea) 172 | { 173 | return YX::starting_yx; 174 | } 175 | if (ea == _ea2yx.rbegin()->first) 176 | { 177 | return max_yx(); 178 | } 179 | 180 | auto it = _ea2yx.upper_bound(ea); 181 | --it; 182 | return it->second; 183 | } 184 | 185 | bool Function::ea_inside(ea_t ea) const 186 | { 187 | return getStart() <= ea && ea < getEnd(); 188 | } 189 | 190 | std::vector> Function::toLines() const 191 | { 192 | std::vector> lines; 193 | 194 | ea_t addr = BADADDR; 195 | std::string line; 196 | for (auto& p : _tokens) 197 | { 198 | if (addr == BADADDR) 199 | { 200 | addr = p.second.ea; 201 | } 202 | 203 | auto& t = p.second; 204 | if (t.kind == Token::Kind::NEW_LINE) 205 | { 206 | lines.emplace_back(std::make_pair(line, addr)); 207 | line.clear(); 208 | addr = BADADDR; 209 | } 210 | else 211 | { 212 | line += t.value; 213 | } 214 | } 215 | 216 | return lines; 217 | } 218 | 219 | std::string Function::toString() const 220 | { 221 | std::stringstream ss; 222 | ss << *this; 223 | return ss.str(); 224 | } 225 | 226 | std::ostream& operator<<(std::ostream& os, const Function& f) 227 | { 228 | os << f.getName() << "<" << std::hex << f.getStart() 229 | << "," << f.getEnd() << ")"; 230 | return os; 231 | } 232 | -------------------------------------------------------------------------------- /src/idaplugin/function.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETDEC_FUNCTION_H 3 | #define RETDEC_FUNCTION_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "token.h" 11 | #include "utils.h" 12 | #include "yx.h" 13 | 14 | /** 15 | * Decompiled function - i.e. its source code. 16 | * The object is XY-aware and EA-aware. 17 | */ 18 | class Function 19 | { 20 | public: 21 | Function(); 22 | Function(func_t* f, const std::vector& tokens); 23 | 24 | func_t* fnc() const; 25 | std::string getName() const; 26 | ea_t getStart() const; 27 | ea_t getEnd() const; 28 | /// Token at YX. 29 | const Token* getToken(YX yx) const; 30 | /// All the tokens. 31 | const std::map& getTokens() const; 32 | 33 | /// YX of the first token. 34 | YX min_yx() const; 35 | /// YX of the last token. 36 | YX max_yx() const; 37 | /// YX of the token before the token on the given YX. 38 | YX prev_yx(YX yx) const; 39 | /// YX of the token after the token on the given YX. 40 | YX next_yx(YX yx) const; 41 | /// [Starting] YX of the token which contains the given YX. 42 | YX adjust_yx(YX yx) const; 43 | /// Entire colored line containing the given YX. 44 | /// I.e. concatenation of all the tokens with y == yx.y 45 | std::string line_yx(YX yx) const; 46 | /// Address of the given YX. 47 | ea_t yx_2_ea(YX yx) const; 48 | /// Addresses of all the XYs with y == yx.y 49 | std::set yx_2_eas(YX yx) const; 50 | /// [The first] XY with the given address. 51 | YX ea_2_yx(ea_t ea) const; 52 | /// Is address inside this function? 53 | bool ea_inside(ea_t ea) const; 54 | 55 | /// Lines with associated addresses. 56 | std::vector> toLines() const; 57 | std::string toString() const; 58 | friend std::ostream& operator<<(std::ostream& os, const Function& f); 59 | 60 | private: 61 | func_t* _fnc = nullptr; 62 | std::map _tokens; 63 | /// Multiple YXs can be associated with the same address. 64 | /// This stores the first such XY. 65 | std::map _ea2yx; 66 | }; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/idaplugin/place.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "retdec.h" 5 | #include "place.h" 6 | 7 | static const idaplace_t _idaplace; 8 | static const retdec_place_t _template(nullptr, YX()); 9 | 10 | void idaapi retdec_place_t::print(qstring* out_buf, void* ud) const 11 | { 12 | qstring ea_str; 13 | ea2str(&ea_str, toea()); 14 | 15 | std::string str = std::string(ea_str.c_str()) 16 | + " @ " 17 | + std::to_string(y()) + ":" + std::to_string(x()); 18 | *out_buf = str.c_str(); 19 | } 20 | 21 | uval_t idaapi retdec_place_t::touval(void* ud) const 22 | { 23 | return y(); 24 | } 25 | 26 | place_t* idaapi retdec_place_t::clone(void) const 27 | { 28 | return new retdec_place_t(*this); 29 | } 30 | 31 | void idaapi retdec_place_t::copyfrom(const place_t* from) 32 | { 33 | auto* p = static_cast(from); 34 | 35 | lnnum = p->lnnum; 36 | _fnc = p->_fnc; 37 | _yx = p->_yx; 38 | } 39 | 40 | place_t* idaapi retdec_place_t::makeplace( 41 | void* ud, 42 | uval_t y, 43 | int lnnum) const 44 | { 45 | auto* p = new retdec_place_t(_fnc, YX(y, 0)); 46 | p->lnnum = lnnum; 47 | return p; 48 | } 49 | 50 | int idaapi retdec_place_t::compare(const place_t* t2) const 51 | { 52 | return compare2(t2, nullptr); 53 | } 54 | 55 | int idaapi retdec_place_t::compare2(const place_t* t2, void *ud) const 56 | { 57 | auto* p = static_cast(t2); 58 | 59 | if (_fnc == p->_fnc) 60 | { 61 | if (yx() < p->yx()) return -1; 62 | else if (yx() > p->yx()) return 1; 63 | else return 0; 64 | } 65 | // I'm not sure if this can happen (i.e. places from different functions 66 | // are compared), but better safe than sorry. 67 | else if (_fnc->getStart() < p->_fnc->getStart()) 68 | { 69 | return -1; 70 | } 71 | else 72 | { 73 | return 1; 74 | } 75 | } 76 | 77 | void idaapi retdec_place_t::adjust(void* ud) 78 | { 79 | // No idea if some handling is needed here. 80 | // It seems to work OK just like this. 81 | // The following is not working: 82 | // _yx = _fnc->adjust_yx(_yx); 83 | // Sometimes it generates some extra empty lines. 84 | _yx.x = 0; 85 | } 86 | 87 | bool idaapi retdec_place_t::prev(void* ud) 88 | { 89 | auto pyx = _fnc->prev_yx(yx()); 90 | if (yx() <= _fnc->min_yx() || pyx == yx()) 91 | { 92 | return false; 93 | } 94 | _yx = pyx; 95 | return true; 96 | } 97 | 98 | bool idaapi retdec_place_t::next(void* ud) 99 | { 100 | auto nyx = _fnc->next_yx(yx()); 101 | if (yx() >= _fnc->max_yx() || nyx == yx()) 102 | { 103 | return false; 104 | } 105 | _yx = nyx; 106 | return true; 107 | } 108 | 109 | bool idaapi retdec_place_t::beginning(void* ud) const 110 | { 111 | return yx() == _fnc->min_yx(); 112 | } 113 | 114 | bool idaapi retdec_place_t::ending(void* ud) const 115 | { 116 | return yx() == _fnc->max_yx(); 117 | } 118 | 119 | int idaapi retdec_place_t::generate( 120 | qstrvec_t* out, 121 | int* out_deflnnum, 122 | color_t* out_pfx_color, 123 | bgcolor_t* out_bgcolor, 124 | void* ud, 125 | int maxsize) const 126 | { 127 | if (maxsize <= 0) 128 | { 129 | return 0; 130 | } 131 | if (x() != 0) 132 | { 133 | return 0; 134 | } 135 | 136 | *out_deflnnum = 0; 137 | 138 | std::string str = _fnc->line_yx(yx()); 139 | out->push_back(str.c_str()); 140 | return 1; 141 | } 142 | 143 | // All members must be serialized and deserialized. 144 | // This is apparently used when places are moved around. 145 | // When I didn't serialize _fnc pointer, I lost the info about it when 146 | // place was set to lochist_entry_t. 147 | // However, this is also used when saving/loading IDB, and so if we store and 148 | // than load function pointer, we are in trouble. Instead we serialize functions 149 | // as their addresses and use decompiler to get an actual function pointer. 150 | void idaapi retdec_place_t::serialize(bytevec_t* out) const 151 | { 152 | place_t__serialize(this, out); 153 | out->pack_ea(_fnc->getStart()); 154 | out->pack_ea(y()); 155 | out->pack_ea(x()); 156 | } 157 | 158 | bool idaapi retdec_place_t::deserialize( 159 | const uchar** pptr, 160 | const uchar* end) 161 | { 162 | if (!place_t__deserialize(this, pptr, end) || *pptr >= end) 163 | { 164 | return false; 165 | } 166 | auto fa = unpack_ea(pptr, end); 167 | _fnc = RetDec::selectiveDecompilation(fa, false); 168 | auto y = unpack_ea(pptr, end); 169 | auto x = unpack_ea(pptr, end); 170 | _yx = YX(y, x); 171 | return true; 172 | } 173 | 174 | int idaapi retdec_place_t::id() const 175 | { 176 | return retdec_place_t::ID; 177 | } 178 | 179 | const char* idaapi retdec_place_t::name() const 180 | { 181 | return retdec_place_t::_name; 182 | } 183 | 184 | ea_t idaapi retdec_place_t::toea() const 185 | { 186 | return _fnc->yx_2_ea(yx()); 187 | } 188 | 189 | bool idaapi retdec_place_t::rebase(const segm_move_infos_t&) 190 | { 191 | // nothing 192 | return false; 193 | } 194 | 195 | place_t* idaapi retdec_place_t::enter(uint32*) const 196 | { 197 | // nothing 198 | return nullptr; 199 | } 200 | 201 | void idaapi retdec_place_t::leave(uint32) const 202 | { 203 | // nothing 204 | } 205 | 206 | int retdec_place_t::ID = -1; 207 | 208 | retdec_place_t::retdec_place_t(Function* fnc, YX yx) 209 | : _fnc(fnc) 210 | , _yx(yx) 211 | { 212 | lnnum = 0; 213 | } 214 | 215 | void retdec_place_t::registerPlace(const plugin_t& PLUGIN) 216 | { 217 | retdec_place_t::ID = register_place_class( 218 | &_template, 219 | PCF_EA_CAPABLE | PCF_MAKEPLACE_ALLOCATES, 220 | &PLUGIN 221 | ); 222 | 223 | /// Register a converter, that will be used for the following reasons: 224 | /// - determine what view can be synchronized with what other view 225 | /// - when views are synchronized, convert the location from one view, 226 | /// into an appropriate location in the other view 227 | /// - if one of p1 or p2 is "idaplace_t", and the other is PCF_EA_CAPABLE, 228 | /// then the converter will also be called when the user wants to jump to 229 | /// an address (e.g., by pressing "g"). In that case, from's place_t's lnnum 230 | /// will be set to -1 (i.e., can be used to descriminate between proper 231 | /// synchronizations, and jump to's if needed.) 232 | /// 233 | /// Note: the converter can be used to convert in both directions, and can be 234 | /// called with its 'from' being of the class of 'p1', or 'p2'. 235 | /// If you want your converter to work in only one direction (e.g., from 236 | /// 'my_dictionary_place_t' -> 'my_definition_place_t'), you can have it 237 | /// return false when it is called with a lochist_entry_t's whose place is 238 | /// of type 'my_definition_place_t'. 239 | /// 240 | /// Note: Whenever one of the 'p1' or 'p2' places is unregistered, 241 | /// corresponding converters will be automatically unregistered as well. 242 | register_loc_converter2( 243 | _template.name(), 244 | _idaplace.name(), 245 | place_converter 246 | ); 247 | } 248 | 249 | YX retdec_place_t::yx() const 250 | { 251 | return _yx; 252 | } 253 | 254 | std::size_t retdec_place_t::y() const 255 | { 256 | return yx().y; 257 | } 258 | 259 | std::size_t retdec_place_t::x() const 260 | { 261 | return yx().x; 262 | } 263 | 264 | const Token* retdec_place_t::token() const 265 | { 266 | return fnc()->getToken(yx()); 267 | } 268 | 269 | Function* retdec_place_t::fnc() const 270 | { 271 | return _fnc; 272 | } 273 | 274 | std::string retdec_place_t::toString() const 275 | { 276 | std::stringstream ss; 277 | ss << *this; 278 | return ss.str(); 279 | } 280 | 281 | std::ostream& operator<<(std::ostream& os, const retdec_place_t& p) 282 | { 283 | os << *p.fnc() << p.yx(); 284 | return os; 285 | } 286 | 287 | lecvt_code_t idaapi place_converter( 288 | lochist_entry_t* dst, 289 | const lochist_entry_t& src, 290 | TWidget* view, 291 | uint32) 292 | { 293 | // idaplace_t -> retdec_place_t 294 | if (src.place()->name() == std::string(_idaplace.name())) 295 | { 296 | auto idaEa = src.place()->toea(); 297 | 298 | auto* cur = dynamic_cast(get_custom_viewer_place( 299 | view, 300 | false, // mouse 301 | nullptr, // x 302 | nullptr // y 303 | )); 304 | if (cur == nullptr) 305 | { 306 | return LECVT_ERROR; 307 | } 308 | 309 | if (cur->fnc()->ea_inside(idaEa)) 310 | { 311 | retdec_place_t p(cur->fnc(), cur->fnc()->ea_2_yx(idaEa)); 312 | dst->set_place(p); 313 | // Set both x and y, see renderer_info_t comment in demo.cpp. 314 | dst->renderer_info().pos.cy = p.y(); 315 | dst->renderer_info().pos.cx = p.x(); 316 | } 317 | else if (Function* fnc = RetDec::selectiveDecompilation(idaEa, false)) 318 | { 319 | retdec_place_t cur(fnc, fnc->ea_2_yx(idaEa)); 320 | dst->set_place(cur); 321 | // Set both x and y, see renderer_info_t comment in demo.cpp. 322 | dst->renderer_info().pos.cy = cur.y(); 323 | dst->renderer_info().pos.cx = cur.x(); 324 | } 325 | else 326 | { 327 | return LECVT_CANCELED; 328 | } 329 | 330 | return LECVT_OK; 331 | } 332 | // retdec_place_t -> idaplace_t 333 | else if (src.place()->name() == std::string(_template.name())) 334 | { 335 | auto* demoPlc = static_cast(src.place()); 336 | idaplace_t p(demoPlc->toea(), 0); 337 | dst->set_place(p); 338 | return LECVT_OK; 339 | } 340 | // should not happen 341 | else 342 | { 343 | return LECVT_CANCELED; 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /src/idaplugin/place.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETDEC_PLACE_H 3 | #define RETDEC_PLACE_H 4 | 5 | #include 6 | 7 | #include "function.h" 8 | #include "retdec.h" 9 | #include "yx.h" 10 | 11 | /** 12 | * Denotes a displayed line. 13 | * 14 | * An object may be displayed on one or more lines. All lines of an object are 15 | * generated at once and kept in a linearray_t class. 16 | */ 17 | class retdec_place_t : public place_t 18 | { 19 | // Inherited from place_t. 20 | // 21 | public: 22 | /// Generate a short description of the location. 23 | /// This description is used on the status bar. 24 | /// \param out_buf the output buffer 25 | /// \param ud pointer to user-defined context data. 26 | /// Is supplied by ::linearray_t 27 | virtual void idaapi print(qstring* out_buf, void* ud) const override; 28 | 29 | /// Map the location to a number. 30 | /// This mapping is used to draw the vertical scrollbar. 31 | /// \param ud pointer to user-defined context data. 32 | /// Is supplied by ::linearray_t 33 | virtual uval_t idaapi touval(void* ud) const override; 34 | 35 | /// Clone the location. 36 | /// \return a pointer to a copy of the current location in dynamic 37 | /// memory 38 | virtual place_t* idaapi clone(void) const override; 39 | 40 | /// Copy the specified location object to the current object 41 | virtual void idaapi copyfrom(const place_t* from) override; 42 | 43 | /// Map a number to a location. 44 | /// When the user clicks on the scrollbar and drags it, we need to 45 | /// determine the location corresponding to the new scrollbar position. 46 | /// This function is used to determine it. It builds a location object 47 | /// for the specified 'x' and returns a pointer to it. 48 | /// \param ud pointer to user-defined context data. 49 | /// Is supplied by ::linearray_t 50 | /// \param x number to map 51 | /// \param lnnum line number to initialize 'lnnum' 52 | /// \return a static object, no need to destroy it. 53 | virtual place_t* idaapi makeplace( 54 | void* ud, 55 | uval_t y, 56 | int lnnum) const override; 57 | 58 | /// Deprecated. Please consider compare2(const place_t *, void *) instead. 59 | virtual int idaapi compare(const place_t* t2) const override; 60 | 61 | /// Compare two locations except line numbers (lnnum). 62 | /// This function is used to organize loops. 63 | /// For example, if the user has selected an range, its boundaries are remembered 64 | /// as location objects. Any operation within the selection will have the following 65 | /// look: for ( loc=starting_location; loc < ending_location; loc.next() ) 66 | /// In this loop, the comparison function is used. 67 | /// \param t2 the place to compare this one to. 68 | /// \param ud pointer to user-defined context data. 69 | /// \retval -1 if the current location is less than 't2' 70 | /// \retval 0 if the current location is equal to than 't2' 71 | /// \retval 1 if the current location is greater than 't2' 72 | virtual int idaapi compare2(const place_t* t2, void* ud) const override; 73 | 74 | /// Adjust the current location to point to a displayable object. 75 | /// This function validates the location and makes sure that it points 76 | /// to an existing object. For example, if the location points to the 77 | /// middle of an instruction, it will be adjusted to point to the 78 | /// beginning of the instruction. 79 | /// \param ud pointer to user-defined context data. 80 | /// Is supplied by ::linearray_t 81 | virtual void idaapi adjust(void* ud) override; 82 | 83 | /// Move to the previous displayable location. 84 | /// \param ud pointer to user-defined context data. 85 | /// Is supplied by ::linearray_t 86 | /// \return success 87 | virtual bool idaapi prev(void* ud) override; 88 | 89 | /// Move to the next displayable location. 90 | /// \param ud pointer to user-defined context data. 91 | /// Is supplied by ::linearray_t 92 | /// \return success 93 | virtual bool idaapi next(void* ud) override; 94 | 95 | /// Are we at the first displayable object?. 96 | /// \param ud pointer to user-defined context data. 97 | /// Is supplied by ::linearray_t 98 | /// \return true if the current location points to the first 99 | /// displayable object 100 | virtual bool idaapi beginning(void* ud) const override; 101 | 102 | /// Are we at the last displayable object?. 103 | /// \param ud pointer to user-defined context data. 104 | /// Is supplied by ::linearray_t 105 | /// \return true if the current location points to the last 106 | /// displayable object 107 | virtual bool idaapi ending(void* ud) const override; 108 | 109 | /// Generate text lines for the current location. 110 | /// \param out storage for the lines 111 | /// \param out_deflnnum pointer to the cell that will contain the num 112 | /// of the most 'interesting' generated line 113 | /// \param out_pfx_color pointer to the cell that will contain the 114 | /// line prefix color 115 | /// \param out_bgcolor pointer to the cell that will contain the 116 | /// background color 117 | /// \param ud pointer to user-defined context data. 118 | /// Is supplied by linearray_t 119 | /// \param maxsize the maximum number of lines to generate 120 | /// \return number of generated lines 121 | virtual int idaapi generate( 122 | qstrvec_t* out, 123 | int* out_deflnnum, 124 | color_t* out_pfx_color, 125 | bgcolor_t* out_bgcolor, 126 | void* ud, 127 | int maxsize) const override; 128 | 129 | /// Serialize this instance. 130 | /// It is fundamental that all instances of a particular subclass 131 | /// of of place_t occupy the same number of bytes when serialized. 132 | /// \param out buffer to serialize into 133 | virtual void idaapi serialize(bytevec_t* out) const override; 134 | 135 | /// De-serialize into this instance. 136 | /// 'pptr' should be incremented by as many bytes as 137 | /// de-serialization consumed. 138 | /// \param pptr pointer to a serialized representation of a place_t 139 | /// of this type. 140 | /// \param end pointer to end of buffer. 141 | /// \return whether de-serialization was successful 142 | virtual bool idaapi deserialize( 143 | const uchar** pptr, 144 | const uchar* end) override; 145 | 146 | /// Get the place's ID (i.e., the value returned by 147 | /// register_place_class()) 148 | /// \return the id 149 | virtual int idaapi id() const override; 150 | 151 | /// Get this place type name. 152 | /// All instances of a given class must return the same string. 153 | /// \return the place type name. Please try and pick something that is 154 | /// not too generic, as it might clash w/ other plugins. A good 155 | /// practice is to prefix the class name with the name 156 | /// of your plugin. E.g., "myplugin:srcplace_t". 157 | virtual const char* idaapi name() const override; 158 | 159 | /// Map the location to an ea_t. 160 | /// \return the corresponding ea_t, or BADADDR; 161 | virtual ea_t idaapi toea() const override; 162 | 163 | /// Rebase the place instance 164 | /// \param infos the segments that were moved 165 | /// \return true if place was rebased, false otherwise 166 | virtual bool idaapi rebase(const segm_move_infos_t&) override; 167 | 168 | /// Visit this place, possibly 'unhiding' a section of text. 169 | /// If entering that place required some expanding, a place_t 170 | /// should be returned that represents that section, plus some 171 | /// flags for later use by 'leave()'. 172 | /// \param out_flags flags to be used together with the place_t that is 173 | /// returned, in order to restore the section to its 174 | /// original state when leave() is called. 175 | /// \return a place_t corresponding to the beginning of the section 176 | /// of text that had to be expanded. That place_t's leave() will 177 | /// be called with the flags contained in 'out_flags' when the 178 | /// user navigates away from it. 179 | virtual place_t* idaapi enter(uint32*) const override; 180 | 181 | /// Leave this place, possibly 'hiding' a section of text that was 182 | /// previously expanded (at enter()-time.) 183 | virtual void idaapi leave(uint32) const override; 184 | 185 | public: 186 | static int ID; 187 | 188 | retdec_place_t(Function* fnc, YX yx); 189 | static void registerPlace(const plugin_t& PLUGIN); 190 | 191 | YX yx() const; 192 | std::size_t y() const; 193 | std::size_t x() const; 194 | const Token* token() const; 195 | Function* fnc() const; 196 | 197 | std::string toString() const; 198 | friend std::ostream& operator<<( 199 | std::ostream& os, 200 | const retdec_place_t& p 201 | ); 202 | 203 | private: 204 | inline static const char* _name = "retdec_place_t"; 205 | 206 | Function* _fnc = nullptr; 207 | YX _yx; 208 | }; 209 | 210 | /// Converts from an entry with a given place type, to another entry, 211 | /// with another place type, to be used with the view 'view'. Typically 212 | /// used when views are synchronized. 213 | /// The 'renderer_info_t' part of 'dst' will be pre-filled with 214 | /// the current renderer_info_t of 'view', while the 'place_t' instance 215 | /// will always be NULL. 216 | /// 217 | /// lochist_entry_cvt_t 218 | /// 219 | lecvt_code_t idaapi place_converter( 220 | lochist_entry_t* dst, 221 | const lochist_entry_t& src, 222 | TWidget* view, 223 | uint32 224 | ); 225 | 226 | #endif 227 | -------------------------------------------------------------------------------- /src/idaplugin/retdec.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "function.h" 6 | #include "config.h" 7 | #include "place.h" 8 | #include "retdec.h" 9 | #include "ui.h" 10 | 11 | plugmod_t* idaapi init(void) 12 | { 13 | auto* ctx = new RetDec(); 14 | return ctx->pluginRegNumber < 0 ? nullptr : ctx; 15 | } 16 | 17 | plugin_t PLUGIN = 18 | { 19 | IDP_INTERFACE_VERSION, 20 | PLUGIN_MULTI, // plugin flags 21 | init, // initialize fnc 22 | nullptr, // terminate fnc 23 | nullptr, // invoke fnc 24 | RetDec::pluginCopyright.data(), // long plugin comment 25 | RetDec::pluginURL.data(), // multiline plugin help 26 | RetDec::pluginName.data(), // the preferred plugin short name 27 | RetDec::pluginHotkey.data() // the preferred plugin hotkey 28 | }; 29 | 30 | std::map RetDec::fnc2fnc; 31 | retdec::config::Config RetDec::config; 32 | 33 | RetDec::RetDec() 34 | { 35 | pluginInfo.id = pluginID.data(); 36 | pluginInfo.name = pluginName.data(); 37 | pluginInfo.producer = pluginProducer.data(); 38 | pluginInfo.version = pluginVersion.data(); 39 | pluginInfo.url = pluginContact.data(); 40 | pluginInfo.freeform = pluginCopyright.data(); 41 | pluginRegNumber = register_addon(&pluginInfo); 42 | if (pluginRegNumber < 0) 43 | { 44 | WARNING_GUI(pluginName << " version " << pluginVersion 45 | << " failed to register.\n"); 46 | return; 47 | } 48 | 49 | if (!register_action(fullDecompilation_ah_desc) 50 | || !attach_action_to_menu( 51 | "File/Produce file/Create DIF file", 52 | fullDecompilation_ah_t::actionName, 53 | SETMENU_APP)) 54 | { 55 | ERROR_MSG("Failed to register: " << fullDecompilation_ah_t::actionName); 56 | } 57 | register_action(jump2asm_ah_desc); 58 | register_action(copy2asm_ah_desc); 59 | register_action(funcComment_ah_desc); 60 | register_action(renameGlobalObj_ah_desc); 61 | register_action(openCalls_ah_desc); 62 | register_action(openXrefs_ah_desc); 63 | register_action(changeFuncType_ah_desc); 64 | 65 | retdec_place_t::registerPlace(PLUGIN); 66 | 67 | hook_event_listener(HT_UI, this); 68 | 69 | INFO_MSG(pluginName << " version " << pluginVersion << " loaded OK\n"); 70 | } 71 | 72 | bool runDecompilation( 73 | retdec::config::Config& config, 74 | std::string* output = nullptr) 75 | { 76 | try 77 | { 78 | auto rc = retdec::decompile(config, output); 79 | if (rc != 0) 80 | { 81 | throw std::runtime_error( 82 | "decompilation error code = " + std::to_string(rc) 83 | ); 84 | } 85 | } 86 | catch (const std::runtime_error& e) 87 | { 88 | WARNING_GUI("Decompilation exception: " << e.what() << std::endl); 89 | return true; 90 | } 91 | catch (...) 92 | { 93 | WARNING_GUI("Decompilation exception: unknown" << std::endl); 94 | return true; 95 | } 96 | 97 | return false; 98 | } 99 | 100 | Function* RetDec::selectiveDecompilation( 101 | ea_t ea, 102 | bool redecompile, 103 | bool regressionTests) 104 | { 105 | if (isRelocatable() && inf_get_min_ea() != 0) 106 | { 107 | WARNING_GUI("RetDec plugin can selectively decompile only " 108 | "relocatable objects loaded at 0x0.\n" 109 | "Rebase the program to 0x0 or use full decompilation." 110 | ); 111 | return nullptr; 112 | } 113 | 114 | func_t* f = get_func(ea); 115 | if (f == nullptr) 116 | { 117 | WARNING_GUI("Function must be selected by the cursor.\n"); 118 | return nullptr; 119 | } 120 | 121 | if (!redecompile) 122 | { 123 | auto it = fnc2fnc.find(f); 124 | if (it != fnc2fnc.end()) 125 | { 126 | return &it->second; 127 | } 128 | } 129 | 130 | if (fillConfig(config)) 131 | { 132 | return nullptr; 133 | } 134 | 135 | std::set selectedFncs; 136 | std::string output; 137 | std::string* out = &output; 138 | 139 | config.parameters.setOutputFormat("json"); 140 | retdec::common::AddressRange r(f->start_ea, f->end_ea); 141 | config.parameters.selectedRanges.insert(r); 142 | selectedFncs.insert(f->start_ea); 143 | config.parameters.setIsSelectedDecodeOnly(true); 144 | 145 | if (regressionTests) 146 | { 147 | config.parameters.setIsVerboseOutput(true); 148 | config.parameters.setOutputFormat("plain"); 149 | config.parameters.setOutputFile(config.parameters.getInputFile() + ".c"); 150 | out = nullptr; 151 | } 152 | 153 | show_wait_box("Decompiling..."); 154 | if (runDecompilation(config, out)) 155 | { 156 | hide_wait_box(); 157 | return nullptr; 158 | } 159 | hide_wait_box(); 160 | 161 | if (out == nullptr) 162 | { 163 | return nullptr; 164 | } 165 | 166 | auto ts = parseTokens(output, f->start_ea); 167 | if (ts.empty()) 168 | { 169 | return nullptr; 170 | } 171 | return &(fnc2fnc[f] = Function(f, ts)); 172 | } 173 | 174 | Function* RetDec::selectiveDecompilationAndDisplay(ea_t ea, bool redecompile) 175 | { 176 | auto* f = selectiveDecompilation(ea, redecompile); 177 | if (f) 178 | { 179 | displayFunction(f, ea); 180 | } 181 | return f; 182 | } 183 | 184 | void RetDec::displayFunction(Function* f, ea_t ea) 185 | { 186 | fnc = f; 187 | 188 | retdec_place_t min(fnc, fnc->min_yx()); 189 | retdec_place_t max(fnc, fnc->max_yx()); 190 | retdec_place_t cur(fnc, fnc->ea_2_yx(ea)); 191 | 192 | TWidget* widget = find_widget(RetDec::pluginName.c_str()); 193 | if (widget != nullptr) 194 | { 195 | set_custom_viewer_range(custViewer, &min, &max); 196 | jumpto(custViewer, &cur, cur.x(), cur.y()); 197 | bool take_focus = true; 198 | activate_widget(custViewer, take_focus); 199 | return; 200 | } 201 | 202 | // Without setting both x and y in render info, the current line gets 203 | // displayed as the first line in the viewer. Which is not nice because we 204 | // don't see the context before it. It is better if it is somewhere in the 205 | // middle of the viewer. 206 | renderer_info_t rinfo; 207 | rinfo.rtype = TCCRT_FLAT; 208 | rinfo.pos.cx = cur.x(); 209 | rinfo.pos.cy = cur.y(); 210 | 211 | custViewer = create_custom_viewer( 212 | RetDec::pluginName.c_str(), // title 213 | &min, // minplace 214 | &max, // maxplace 215 | &cur, // curplace 216 | &rinfo, // rinfo 217 | this, // ud 218 | &ui_handlers, // handlers 219 | this, // cvhandlers_ud 220 | nullptr // parent widget 221 | ); 222 | set_view_renderer_type(custViewer, TCCRT_FLAT); 223 | 224 | codeViewer = create_code_viewer(custViewer); 225 | set_code_viewer_is_source(codeViewer); 226 | display_widget(codeViewer, WOPN_DP_TAB | WOPN_RESTORE); 227 | 228 | return; 229 | } 230 | 231 | bool RetDec::fullDecompilation() 232 | { 233 | std::string defaultOut = getInputPath() + ".c"; 234 | 235 | char *tmp = ask_file( // Returns: file name 236 | true, // bool for_saving 237 | defaultOut.data(), // const char *default_answer 238 | "%s", // const char *format 239 | "Save decompiled file" 240 | ); 241 | if (tmp == nullptr) // canceled 242 | { 243 | return false; 244 | } 245 | std::string out = tmp; 246 | 247 | INFO_MSG("Selected file: " << out << "\n"); 248 | 249 | if (fillConfig(config, out)) 250 | { 251 | return false; 252 | } 253 | config.parameters.setOutputFormat("c"); 254 | 255 | show_wait_box("Decompiling..."); 256 | runDecompilation(config); 257 | hide_wait_box(); 258 | 259 | return true; 260 | } 261 | 262 | bool idaapi RetDec::run(size_t arg) 263 | { 264 | if (!auto_is_ok()) 265 | { 266 | INFO_MSG("RetDec plugin cannot run because the initial autoanalysis" 267 | " has not been finished.\n"); 268 | return false; 269 | } 270 | 271 | // ordinary selective decompilation 272 | // 273 | if (arg == 0) 274 | { 275 | auto* cv = get_current_viewer(); 276 | bool redecompile = cv == custViewer || cv == codeViewer; 277 | return selectiveDecompilationAndDisplay(get_screen_ea(), redecompile); 278 | } 279 | // ordinary full decompilation 280 | // 281 | else if (arg == 1) 282 | { 283 | return fullDecompilation(); 284 | } 285 | // regression tests selective decompilation 286 | // function to decompile is marked by "" string in comment 287 | // 288 | else if (arg == 2) 289 | { 290 | for (unsigned i = 0; i < get_func_qty(); ++i) 291 | { 292 | qstring qCmt; 293 | func_t *fnc = getn_func(i); 294 | if (get_func_cmt(&qCmt, fnc, false) <= 0) 295 | { 296 | continue; 297 | } 298 | 299 | std::string cmt = qCmt.c_str();; 300 | if (cmt.find("") != std::string::npos) 301 | { 302 | auto r = selectiveDecompilation( 303 | fnc->start_ea, 304 | false, // redecompile 305 | true); // regressionTests 306 | return r; 307 | } 308 | } 309 | return true; 310 | } 311 | // regression tests full decompilation 312 | // 313 | else if (arg == 3) 314 | { 315 | return fullDecompilation(); 316 | } 317 | else 318 | { 319 | WARNING_GUI(pluginName << " version " << pluginVersion 320 | << " cannot handle argument '" << arg << "'.\n" 321 | ); 322 | return false; 323 | } 324 | 325 | return true; 326 | } 327 | 328 | RetDec::~RetDec() 329 | { 330 | unhook_event_listener(HT_UI, this); 331 | } 332 | 333 | void RetDec::modifyFunctions( 334 | Token::Kind k, 335 | const std::string& oldVal, 336 | const std::string& newVal) 337 | { 338 | for (auto& p : fnc2fnc) 339 | { 340 | modifyFunction(p.first, k, oldVal, newVal); 341 | } 342 | } 343 | 344 | void RetDec::modifyFunction( 345 | func_t* f, 346 | Token::Kind k, 347 | const std::string& oldVal, 348 | const std::string& newVal) 349 | { 350 | auto fIt = fnc2fnc.find(f); 351 | if (fIt == fnc2fnc.end()) 352 | { 353 | return; 354 | } 355 | Function& F = fIt->second; 356 | 357 | std::vector newTokens; 358 | 359 | for (auto& t : F.getTokens()) 360 | { 361 | if (t.second.kind == k && t.second.value == oldVal) 362 | { 363 | newTokens.emplace_back(Token(k, t.second.ea, newVal)); 364 | } 365 | else 366 | { 367 | newTokens.emplace_back(t.second); 368 | } 369 | } 370 | 371 | fIt->second = Function(f, newTokens); 372 | } 373 | 374 | ea_t RetDec::getFunctionEa(const std::string& name) 375 | { 376 | // USe config. 377 | auto* f = config.functions.getFunctionByName(name); 378 | if (f && f->getStart().isDefined()) 379 | { 380 | return f->getStart(); 381 | } 382 | 383 | // Use IDA. 384 | for (unsigned i = 0; i < get_func_qty(); ++i) 385 | { 386 | func_t* f = getn_func(i); 387 | qstring qFncName; 388 | get_func_name(&qFncName, f->start_ea); 389 | if (qFncName.c_str() == name) 390 | { 391 | return f->start_ea; 392 | } 393 | } 394 | 395 | return BADADDR; 396 | } 397 | 398 | func_t* RetDec::getIdaFunction(const std::string& name) 399 | { 400 | auto ea = getFunctionEa(name); 401 | return ea != BADADDR ? get_func(ea) : nullptr; 402 | } 403 | 404 | ea_t RetDec::getGlobalVarEa(const std::string& name) 405 | { 406 | auto* g = config.globals.getObjectByName(name); 407 | if (g && g->getStorage().getAddress()) 408 | { 409 | return g->getStorage().getAddress(); 410 | } 411 | return BADADDR; 412 | } 413 | -------------------------------------------------------------------------------- /src/idaplugin/retdec.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETDEC_RETDEC_H 3 | #define RETDEC_RETDEC_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "function.h" 17 | #include "ui.h" 18 | #include "utils.h" 19 | 20 | /** 21 | * Plugin's global data. 22 | */ 23 | class RetDec : public plugmod_t, public event_listener_t 24 | { 25 | // Inherited. 26 | // 27 | public: 28 | RetDec(); 29 | virtual ~RetDec(); 30 | 31 | virtual bool idaapi run(size_t) override; 32 | virtual ssize_t idaapi on_event(ssize_t code, va_list va) override; 33 | 34 | // Plugin information. 35 | // 36 | public: 37 | inline static const std::string pluginName = "RetDec"; 38 | inline static const std::string pluginID = "avast.retdec"; 39 | inline static const std::string pluginProducer = "Avast Software"; 40 | inline static const std::string pluginCopyright = "Copyright 2020 " + pluginProducer; 41 | inline static const std::string pluginEmail = "support@retdec.com"; 42 | inline static const std::string pluginURL = "https://retdec.com/"; 43 | inline static const std::string pluginRetDecGithub = "https://github.com/avast/retdec"; 44 | inline static const std::string pluginGithub = "https://github.com/avast/retdec-idaplugin"; 45 | inline static const std::string pluginContact = pluginURL + "\nEMAIL: " + pluginEmail; 46 | inline static const std::string pluginVersion = RELEASE_VERSION; 47 | inline static const std::string pluginHotkey = "Ctrl-d"; 48 | inline static const std::string pluginBuildDate = retdec::utils::getCurrentDate(); 49 | /// Plugin information showed in the About box. 50 | addon_info_t pluginInfo; 51 | int pluginRegNumber = -1; 52 | 53 | // Decompilation. 54 | // 55 | public: 56 | static bool fullDecompilation(); 57 | static Function* selectiveDecompilation( 58 | ea_t ea, 59 | bool redecompile, 60 | bool regressionTests = false 61 | ); 62 | 63 | Function* selectiveDecompilationAndDisplay(ea_t ea, bool redecompile); 64 | void displayFunction(Function* f, ea_t ea); 65 | 66 | void modifyFunctions( 67 | Token::Kind k, 68 | const std::string& oldVal, 69 | const std::string& newVal 70 | ); 71 | void modifyFunction( 72 | func_t* f, 73 | Token::Kind k, 74 | const std::string& oldVal, 75 | const std::string& newVal 76 | ); 77 | 78 | ea_t getFunctionEa(const std::string& name); 79 | func_t* getIdaFunction(const std::string& name); 80 | ea_t getGlobalVarEa(const std::string& name); 81 | 82 | /// Currently displayed function. 83 | Function* fnc = nullptr; 84 | 85 | /// All the decompiled functions. 86 | static std::map fnc2fnc; 87 | 88 | /// Decompilation config. 89 | static retdec::config::Config config; 90 | 91 | // UI. 92 | // 93 | public: 94 | TWidget* custViewer = nullptr; 95 | TWidget* codeViewer = nullptr; 96 | 97 | fullDecompilation_ah_t fullDecompilation_ah = fullDecompilation_ah_t(*this); 98 | const action_desc_t fullDecompilation_ah_desc = ACTION_DESC_LITERAL_PLUGMOD( 99 | fullDecompilation_ah_t::actionName, 100 | fullDecompilation_ah_t::actionLabel, 101 | &fullDecompilation_ah, 102 | this, 103 | fullDecompilation_ah_t::actionHotkey, 104 | nullptr, 105 | -1 106 | ); 107 | 108 | jump2asm_ah_t jump2asm_ah = jump2asm_ah_t(*this); 109 | const action_desc_t jump2asm_ah_desc = ACTION_DESC_LITERAL_PLUGMOD( 110 | jump2asm_ah_t::actionName, 111 | jump2asm_ah_t::actionLabel, 112 | &jump2asm_ah, 113 | this, 114 | jump2asm_ah_t::actionHotkey, 115 | nullptr, 116 | -1 117 | ); 118 | 119 | copy2asm_ah_t copy2asm_ah = copy2asm_ah_t(*this); 120 | const action_desc_t copy2asm_ah_desc = ACTION_DESC_LITERAL_PLUGMOD( 121 | copy2asm_ah_t::actionName, 122 | copy2asm_ah_t::actionLabel, 123 | ©2asm_ah, 124 | this, 125 | copy2asm_ah_t::actionHotkey, 126 | nullptr, 127 | -1 128 | ); 129 | 130 | funcComment_ah_t funcComment_ah = funcComment_ah_t(*this); 131 | const action_desc_t funcComment_ah_desc = ACTION_DESC_LITERAL_PLUGMOD( 132 | funcComment_ah_t::actionName, 133 | funcComment_ah_t::actionLabel, 134 | &funcComment_ah, 135 | this, 136 | funcComment_ah_t::actionHotkey, 137 | nullptr, 138 | -1 139 | ); 140 | 141 | renameGlobalObj_ah_t renameGlobalObj_ah = renameGlobalObj_ah_t(*this); 142 | const action_desc_t renameGlobalObj_ah_desc = ACTION_DESC_LITERAL_PLUGMOD( 143 | renameGlobalObj_ah_t::actionName, 144 | renameGlobalObj_ah_t::actionLabel, 145 | &renameGlobalObj_ah, 146 | this, 147 | renameGlobalObj_ah_t::actionHotkey, 148 | nullptr, 149 | -1 150 | ); 151 | 152 | openXrefs_ah_t openXrefs_ah = openXrefs_ah_t(*this); 153 | const action_desc_t openXrefs_ah_desc = ACTION_DESC_LITERAL_PLUGMOD( 154 | openXrefs_ah_t::actionName, 155 | openXrefs_ah_t::actionLabel, 156 | &openXrefs_ah, 157 | this, 158 | openXrefs_ah_t::actionHotkey, 159 | nullptr, 160 | -1 161 | ); 162 | 163 | openCalls_ah_t openCalls_ah = openCalls_ah_t(*this); 164 | const action_desc_t openCalls_ah_desc = ACTION_DESC_LITERAL_PLUGMOD( 165 | openCalls_ah_t::actionName, 166 | openCalls_ah_t::actionLabel, 167 | &openCalls_ah, 168 | this, 169 | openCalls_ah_t::actionHotkey, 170 | nullptr, 171 | -1 172 | ); 173 | 174 | changeFuncType_ah_t changeFuncType_ah = changeFuncType_ah_t(*this); 175 | const action_desc_t changeFuncType_ah_desc = ACTION_DESC_LITERAL_PLUGMOD( 176 | changeFuncType_ah_t::actionName, 177 | changeFuncType_ah_t::actionLabel, 178 | &changeFuncType_ah, 179 | this, 180 | changeFuncType_ah_t::actionHotkey, 181 | nullptr, 182 | -1 183 | ); 184 | }; 185 | 186 | #endif -------------------------------------------------------------------------------- /src/idaplugin/token.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "token.h" 13 | 14 | std::map TokenColors = 15 | { 16 | {Token::Kind::NEW_LINE, SCOLOR_DEFAULT}, 17 | {Token::Kind::WHITE_SPACE, SCOLOR_DEFAULT}, 18 | {Token::Kind::PUNCTUATION, SCOLOR_KEYWORD}, 19 | {Token::Kind::OPERATOR, SCOLOR_KEYWORD}, 20 | {Token::Kind::ID_GVAR, SCOLOR_DREF}, 21 | {Token::Kind::ID_LVAR, SCOLOR_DREF}, 22 | {Token::Kind::ID_MEM, SCOLOR_DREF}, 23 | {Token::Kind::ID_LAB, SCOLOR_DREF}, 24 | {Token::Kind::ID_FNC, SCOLOR_DEFAULT}, 25 | {Token::Kind::ID_ARG, SCOLOR_DREF}, 26 | {Token::Kind::KEYWORD, SCOLOR_MACRO}, 27 | {Token::Kind::TYPE, SCOLOR_MACRO}, 28 | {Token::Kind::PREPROCESSOR, SCOLOR_AUTOCMT}, 29 | {Token::Kind::INCLUDE, SCOLOR_NUMBER}, 30 | {Token::Kind::LITERAL_BOOL, SCOLOR_NUMBER}, 31 | {Token::Kind::LITERAL_INT, SCOLOR_NUMBER}, 32 | {Token::Kind::LITERAL_FP, SCOLOR_NUMBER}, 33 | {Token::Kind::LITERAL_STR, SCOLOR_NUMBER}, 34 | {Token::Kind::LITERAL_SYM, SCOLOR_NUMBER}, 35 | {Token::Kind::LITERAL_PTR, SCOLOR_NUMBER}, 36 | {Token::Kind::COMMENT, SCOLOR_AUTOCMT}, 37 | }; 38 | 39 | std::map TokenKindStrings = 40 | { 41 | {Token::Kind::NEW_LINE, "NEW_LINE"}, 42 | {Token::Kind::WHITE_SPACE, "WHITE_SPACE"}, 43 | {Token::Kind::PUNCTUATION, "PUNCTUATION"}, 44 | {Token::Kind::OPERATOR, "OPERATOR"}, 45 | {Token::Kind::ID_GVAR, "ID_GVAR"}, 46 | {Token::Kind::ID_LVAR, "ID_LVAR"}, 47 | {Token::Kind::ID_MEM, "ID_MEM"}, 48 | {Token::Kind::ID_LAB, "ID_LAB"}, 49 | {Token::Kind::ID_FNC, "ID_FNC"}, 50 | {Token::Kind::ID_ARG, "ID_ARG"}, 51 | {Token::Kind::KEYWORD, "KEYWORD"}, 52 | {Token::Kind::TYPE, "TYPE"}, 53 | {Token::Kind::PREPROCESSOR, "PREPROCESSOR"}, 54 | {Token::Kind::INCLUDE, "INCLUDE"}, 55 | {Token::Kind::LITERAL_BOOL, "LITERAL_BOOL"}, 56 | {Token::Kind::LITERAL_INT, "LITERAL_INT"}, 57 | {Token::Kind::LITERAL_FP, "LITERAL_FP"}, 58 | {Token::Kind::LITERAL_STR, "LITERAL_STR"}, 59 | {Token::Kind::LITERAL_SYM, "LITERAL_SYM"}, 60 | {Token::Kind::LITERAL_PTR, "LITERAL_PTR"}, 61 | {Token::Kind::COMMENT, "COMMENT"}, 62 | }; 63 | 64 | Token::Token() 65 | { 66 | 67 | } 68 | 69 | Token::Token(Kind k, ea_t a, const std::string& v) 70 | : kind(k) 71 | , ea(a) 72 | , value(v) 73 | { 74 | 75 | } 76 | 77 | const std::string& Token::getKindString() const 78 | { 79 | return TokenKindStrings[kind]; 80 | } 81 | 82 | const std::string& Token::getColorTag() const 83 | { 84 | return TokenColors[kind]; 85 | } 86 | 87 | std::vector parseTokens(const std::string& json, ea_t defaultEa) 88 | { 89 | std::vector res; 90 | 91 | rapidjson::StringStream rss(json.c_str()); 92 | rapidjson::Document d; 93 | rapidjson::ParseResult ok = d.ParseStream(rss); 94 | if (!ok) 95 | { 96 | std::string errMsg = GetParseError_En(ok.Code()); 97 | WARNING_GUI("Unable to parse decompilation output: " 98 | << errMsg << std::endl 99 | ); 100 | return res; 101 | } 102 | 103 | auto tokens = d.FindMember("tokens"); 104 | if (tokens == d.MemberEnd() || !tokens->value.IsArray()) 105 | { 106 | WARNING_GUI("Unable to parse tokens from decompilation output.\n"); 107 | return res; 108 | } 109 | 110 | ea_t ea = defaultEa; 111 | 112 | for (auto i = tokens->value.Begin(), e = tokens->value.End(); i != e; ++i) 113 | { 114 | auto& obj = *i; 115 | if (obj.IsNull()) 116 | { 117 | continue; 118 | } 119 | 120 | auto addr = obj.FindMember("addr"); 121 | if (addr != obj.MemberEnd() && addr->value.IsString()) 122 | { 123 | retdec::common::Address a(addr->value.GetString()); 124 | ea = a.isDefined() ? a.getValue() : defaultEa; 125 | } 126 | auto kind = obj.FindMember("kind"); 127 | auto val = obj.FindMember("val"); 128 | if (kind != obj.MemberEnd() && kind->value.IsString() 129 | && val != obj.MemberEnd() && val->value.IsString()) 130 | { 131 | Token::Kind kk; 132 | std::string k = kind->value.GetString(); 133 | if (k == "nl") kk = Token::Kind::NEW_LINE; 134 | else if (k == "ws") kk = Token::Kind::WHITE_SPACE; 135 | else if (k == "punc") kk = Token::Kind::PUNCTUATION; 136 | else if (k == "op") kk = Token::Kind::OPERATOR; 137 | else if (k == "i_gvar") kk = Token::Kind::ID_GVAR; 138 | else if (k == "i_lvar") kk = Token::Kind::ID_LVAR; 139 | else if (k == "i_mem") kk = Token::Kind::ID_MEM; 140 | else if (k == "i_lab") kk = Token::Kind::ID_LAB; 141 | else if (k == "i_fnc") kk = Token::Kind::ID_FNC; 142 | else if (k == "i_arg") kk = Token::Kind::ID_ARG; 143 | else if (k == "keyw") kk = Token::Kind::KEYWORD; 144 | else if (k == "type") kk = Token::Kind::TYPE; 145 | else if (k == "preproc") kk = Token::Kind::PREPROCESSOR; 146 | else if (k == "inc") kk = Token::Kind::INCLUDE; 147 | else if (k == "l_bool") kk = Token::Kind::LITERAL_BOOL; 148 | else if (k == "l_int") kk = Token::Kind::LITERAL_INT; 149 | else if (k == "l_fp") kk = Token::Kind::LITERAL_FP; 150 | else if (k == "l_str") kk = Token::Kind::LITERAL_STR; 151 | else if (k == "l_sym") kk = Token::Kind::LITERAL_SYM; 152 | else if (k == "l_ptr") kk = Token::Kind::LITERAL_PTR; 153 | else if (k == "cmnt") kk = Token::Kind::COMMENT; 154 | else continue; 155 | 156 | res.emplace_back(Token(kk, ea, val->value.GetString())); 157 | } 158 | } 159 | 160 | return res; 161 | } 162 | -------------------------------------------------------------------------------- /src/idaplugin/token.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETDEC_TOKEN_H 3 | #define RETDEC_TOKEN_H 4 | 5 | #include 6 | 7 | #include "utils.h" 8 | 9 | /** 10 | * One element (lexical unit) in the decompiled source code. 11 | * 12 | * Closely related to: 13 | * https://github.com/avast/retdec/wiki/Decompiler-outputs#json-output-file-format 14 | */ 15 | struct Token 16 | { 17 | enum class Kind 18 | { 19 | NEW_LINE = 0, 20 | WHITE_SPACE, 21 | PUNCTUATION, 22 | OPERATOR, 23 | ID_GVAR, 24 | ID_LVAR, 25 | ID_MEM, 26 | ID_LAB, 27 | ID_FNC, 28 | ID_ARG, 29 | KEYWORD, 30 | TYPE, 31 | PREPROCESSOR, 32 | INCLUDE, 33 | LITERAL_BOOL, 34 | LITERAL_INT, 35 | LITERAL_FP, 36 | LITERAL_STR, 37 | LITERAL_SYM, 38 | LITERAL_PTR, 39 | COMMENT, 40 | }; 41 | 42 | Kind kind; 43 | ea_t ea; 44 | std::string value; 45 | 46 | Token(); 47 | Token(Kind k, ea_t a, const std::string& v); 48 | 49 | const std::string& getKindString() const; 50 | const std::string& getColorTag() const; 51 | }; 52 | 53 | std::vector parseTokens(const std::string& json, ea_t defaultEa); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/idaplugin/ui.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "config.h" 3 | #include "place.h" 4 | #include "retdec.h" 5 | #include "ui.h" 6 | 7 | // 8 | //============================================================================== 9 | // fullDecompilation_ah_t 10 | //============================================================================== 11 | // 12 | 13 | fullDecompilation_ah_t::fullDecompilation_ah_t(RetDec& p) 14 | : plg(p) 15 | { 16 | 17 | } 18 | 19 | int idaapi fullDecompilation_ah_t::activate(action_activation_ctx_t*) 20 | { 21 | plg.fullDecompilation(); 22 | return false; 23 | } 24 | 25 | action_state_t idaapi fullDecompilation_ah_t::update(action_update_ctx_t*) 26 | { 27 | return AST_ENABLE_ALWAYS; 28 | } 29 | 30 | // 31 | //============================================================================== 32 | // jump2asm_ah_t 33 | //============================================================================== 34 | // 35 | 36 | jump2asm_ah_t::jump2asm_ah_t(RetDec& p) 37 | : plg(p) 38 | { 39 | 40 | } 41 | 42 | int idaapi jump2asm_ah_t::activate(action_activation_ctx_t* ctx) 43 | { 44 | auto* place = dynamic_cast(get_custom_viewer_place( 45 | ctx->widget, 46 | false, // mouse 47 | nullptr, // x 48 | nullptr // y 49 | )); 50 | if (place == nullptr) 51 | { 52 | return false; 53 | } 54 | 55 | jumpto(place->toea(), 0, UIJMP_ACTIVATE | UIJMP_IDAVIEW); 56 | return false; 57 | } 58 | 59 | action_state_t idaapi jump2asm_ah_t::update(action_update_ctx_t* ctx) 60 | { 61 | return ctx->widget == plg.custViewer 62 | ? AST_ENABLE_FOR_WIDGET : AST_DISABLE_FOR_WIDGET; 63 | } 64 | 65 | // 66 | //============================================================================== 67 | // copy2asm_ah_t 68 | //============================================================================== 69 | // 70 | 71 | copy2asm_ah_t::copy2asm_ah_t(RetDec& p) 72 | : plg(p) 73 | { 74 | 75 | } 76 | 77 | int idaapi copy2asm_ah_t::activate(action_activation_ctx_t*) 78 | { 79 | static const char* text = "Copying pseudocode to disassembly" 80 | " will destroy existing comments.\n" 81 | "Do you want to continue?"; 82 | if (ask_yn(ASKBTN_NO, text) == ASKBTN_YES) 83 | { 84 | for (auto& p : plg.fnc->toLines()) 85 | { 86 | ea_t addr = p.second; 87 | auto& line = p.first; 88 | 89 | delete_extra_cmts(addr, E_PREV); 90 | bool anteriorCmt = true; 91 | add_extra_cmt(addr, anteriorCmt, "%s", line.c_str()); 92 | } 93 | 94 | // Focus to IDA view. 95 | auto* place = dynamic_cast(get_custom_viewer_place( 96 | plg.custViewer, 97 | false, // mouse 98 | nullptr, // x 99 | nullptr // y 100 | )); 101 | if (place != nullptr) 102 | { 103 | jumpto(place->toea(), 0, UIJMP_ACTIVATE | UIJMP_IDAVIEW); 104 | } 105 | } 106 | return false; 107 | } 108 | 109 | action_state_t idaapi copy2asm_ah_t::update(action_update_ctx_t* ctx) 110 | { 111 | return ctx->widget == plg.custViewer 112 | ? AST_ENABLE_FOR_WIDGET : AST_DISABLE_FOR_WIDGET; 113 | } 114 | 115 | // 116 | //============================================================================== 117 | // funcComment_ah_t 118 | //============================================================================== 119 | // 120 | 121 | funcComment_ah_t::funcComment_ah_t(RetDec& p) 122 | : plg(p) 123 | { 124 | 125 | } 126 | 127 | int idaapi funcComment_ah_t::activate(action_activation_ctx_t*) 128 | { 129 | auto* fnc = plg.fnc ? plg.fnc->fnc() : nullptr; 130 | if (fnc == nullptr) 131 | { 132 | return false; 133 | } 134 | 135 | qstring qCmt; 136 | get_func_cmt(&qCmt, fnc, false); 137 | 138 | qstring buff; 139 | if (ask_text( 140 | &buff, 141 | MAXSTR, 142 | qCmt.c_str(), 143 | "Please enter function comment (max %d characters)", 144 | MAXSTR)) 145 | { 146 | set_func_cmt(fnc, buff.c_str(), false); 147 | plg.selectiveDecompilationAndDisplay(fnc->start_ea, true); 148 | } 149 | 150 | return false; 151 | } 152 | 153 | action_state_t idaapi funcComment_ah_t::update(action_update_ctx_t* ctx) 154 | { 155 | return ctx->widget == plg.custViewer 156 | ? AST_ENABLE_FOR_WIDGET : AST_DISABLE_FOR_WIDGET; 157 | } 158 | 159 | // 160 | //============================================================================== 161 | // renameGlobalObj_ah_t 162 | //============================================================================== 163 | // 164 | 165 | renameGlobalObj_ah_t::renameGlobalObj_ah_t(RetDec& p) 166 | : plg(p) 167 | { 168 | 169 | } 170 | 171 | int idaapi renameGlobalObj_ah_t::activate(action_activation_ctx_t* ctx) 172 | { 173 | auto* place = dynamic_cast(get_custom_viewer_place( 174 | ctx->widget, 175 | false, // mouse 176 | nullptr, // x 177 | nullptr // y 178 | )); 179 | auto* token = place ? place->token() : nullptr; 180 | if (token == nullptr) 181 | { 182 | return false; 183 | } 184 | 185 | std::string askString; 186 | ea_t addr = BADADDR; 187 | if (token->kind == Token::Kind::ID_FNC) 188 | { 189 | askString = "Please enter function name"; 190 | addr = plg.getFunctionEa(token->value); 191 | } 192 | else if (token->kind == Token::Kind::ID_GVAR) 193 | { 194 | askString = "Please enter global variable name"; 195 | addr = plg.getGlobalVarEa(token->value); 196 | } 197 | if (addr == BADADDR) 198 | { 199 | return false; 200 | } 201 | 202 | qstring qNewName = token->value.c_str(); 203 | if (!ask_str(&qNewName, HIST_IDENT, "%s", askString.c_str()) 204 | || qNewName.empty()) 205 | { 206 | return false; 207 | } 208 | std::string newName = qNewName.c_str(); 209 | if (newName == token->value) 210 | { 211 | return false; 212 | } 213 | 214 | if (set_name(addr, newName.c_str()) == false) 215 | { 216 | return false; 217 | } 218 | 219 | std::string oldName = token->value; 220 | plg.modifyFunctions(token->kind, oldName, newName); 221 | fillConfig(plg.config); 222 | 223 | return false; 224 | } 225 | 226 | action_state_t idaapi renameGlobalObj_ah_t::update(action_update_ctx_t* ctx) 227 | { 228 | return ctx->widget == plg.custViewer 229 | ? AST_ENABLE_FOR_WIDGET : AST_DISABLE_FOR_WIDGET; 230 | } 231 | 232 | // 233 | //============================================================================== 234 | // openXrefs_ah_t 235 | //============================================================================== 236 | // 237 | 238 | openXrefs_ah_t::openXrefs_ah_t(RetDec& p) 239 | : plg(p) 240 | { 241 | 242 | } 243 | 244 | int idaapi openXrefs_ah_t::activate(action_activation_ctx_t* ctx) 245 | { 246 | auto* place = dynamic_cast(get_custom_viewer_place( 247 | ctx->widget, 248 | false, // mouse 249 | nullptr, // x 250 | nullptr // y 251 | )); 252 | auto* token = place ? place->token() : nullptr; 253 | if (token == nullptr) 254 | { 255 | return false; 256 | } 257 | 258 | ea_t ea = BADADDR; 259 | if (token->kind == Token::Kind::ID_FNC) 260 | { 261 | ea = plg.getFunctionEa(token->value); 262 | } 263 | else if (token->kind == Token::Kind::ID_GVAR) 264 | { 265 | ea = plg.getGlobalVarEa(token->value); 266 | } 267 | if (ea == BADADDR) 268 | { 269 | return false; 270 | } 271 | 272 | open_xrefs_window(ea); 273 | return false; 274 | } 275 | 276 | action_state_t idaapi openXrefs_ah_t::update(action_update_ctx_t* ctx) 277 | { 278 | return ctx->widget == plg.custViewer 279 | ? AST_ENABLE_FOR_WIDGET : AST_DISABLE_FOR_WIDGET; 280 | } 281 | 282 | // 283 | //============================================================================== 284 | // openCalls_ah_t 285 | //============================================================================== 286 | // 287 | 288 | openCalls_ah_t::openCalls_ah_t(RetDec& p) 289 | : plg(p) 290 | { 291 | 292 | } 293 | 294 | int idaapi openCalls_ah_t::activate(action_activation_ctx_t* ctx) 295 | { 296 | auto* place = dynamic_cast(get_custom_viewer_place( 297 | ctx->widget, 298 | false, // mouse 299 | nullptr, // x 300 | nullptr // y 301 | )); 302 | auto* token = place ? place->token() : nullptr; 303 | if (token == nullptr) 304 | { 305 | return false; 306 | } 307 | 308 | ea_t ea = BADADDR; 309 | if (token->kind == Token::Kind::ID_FNC) 310 | { 311 | ea = plg.getFunctionEa(token->value); 312 | } 313 | else if (token->kind == Token::Kind::ID_GVAR) 314 | { 315 | ea = plg.getGlobalVarEa(token->value); 316 | } 317 | if (ea == BADADDR) 318 | { 319 | return false; 320 | } 321 | 322 | open_calls_window(ea); 323 | return false; 324 | } 325 | 326 | action_state_t idaapi openCalls_ah_t::update(action_update_ctx_t* ctx) 327 | { 328 | return ctx->widget == plg.custViewer 329 | ? AST_ENABLE_FOR_WIDGET : AST_DISABLE_FOR_WIDGET; 330 | } 331 | 332 | // 333 | //============================================================================== 334 | // changeFuncType_ah_t 335 | //============================================================================== 336 | // 337 | 338 | changeFuncType_ah_t::changeFuncType_ah_t(RetDec& p) 339 | : plg(p) 340 | { 341 | 342 | } 343 | 344 | int idaapi changeFuncType_ah_t::activate(action_activation_ctx_t* ctx) 345 | { 346 | auto* place = dynamic_cast(get_custom_viewer_place( 347 | ctx->widget, 348 | false, // mouse 349 | nullptr, // x 350 | nullptr // y 351 | )); 352 | auto* token = place ? place->token() : nullptr; 353 | if (token == nullptr) 354 | { 355 | return false; 356 | } 357 | 358 | func_t* fnc = nullptr; 359 | if (token->kind == Token::Kind::ID_FNC) 360 | { 361 | fnc = plg.getIdaFunction(token->value); 362 | } 363 | if (fnc == nullptr) 364 | { 365 | return false; 366 | } 367 | 368 | qstring buf; 369 | int flags = PRTYPE_1LINE | PRTYPE_SEMI; 370 | if (!print_type(&buf, fnc->start_ea, flags)) 371 | { 372 | qstring qFncName; 373 | get_func_name(&qFncName, fnc->start_ea);; 374 | WARNING_GUI("Cannot change declaration for: " 375 | << qFncName.c_str() << "\n" 376 | ); 377 | } 378 | 379 | std::string askString = "Please enter type declaration:"; 380 | 381 | qstring qNewDeclr = buf; 382 | if (!ask_str(&qNewDeclr, HIST_IDENT, "%s", askString.c_str()) 383 | || qNewDeclr.empty()) 384 | { 385 | return false; 386 | } 387 | 388 | if (apply_cdecl(nullptr, fnc->start_ea, qNewDeclr.c_str())) 389 | { 390 | plg.selectiveDecompilationAndDisplay(fnc->start_ea, true); 391 | } 392 | else 393 | { 394 | WARNING_GUI("Cannot change declaration to: " 395 | << qNewDeclr.c_str() << "\n" 396 | ); 397 | } 398 | 399 | return false; 400 | } 401 | 402 | action_state_t idaapi changeFuncType_ah_t::update(action_update_ctx_t* ctx) 403 | { 404 | return ctx->widget == plg.custViewer 405 | ? AST_ENABLE_FOR_WIDGET : AST_DISABLE_FOR_WIDGET; 406 | } 407 | 408 | // 409 | //============================================================================== 410 | // on_event 411 | //============================================================================== 412 | // 413 | 414 | /** 415 | * User interface hook. 416 | */ 417 | ssize_t idaapi RetDec::on_event(ssize_t code, va_list va) 418 | { 419 | switch (code) 420 | { 421 | // IDA is populating the RetDec menu (right-click menu) for a widget. 422 | // We can attach action to popup - i.e. create menu on the fly. 423 | case ui_populating_widget_popup: 424 | { 425 | // Continue only if event was triggered in our widget. 426 | TWidget* view = va_arg(va, TWidget*); 427 | TPopupMenu* popup = va_arg(va, TPopupMenu*); 428 | if (view != custViewer && view != codeViewer) 429 | { 430 | return false; 431 | } 432 | 433 | auto* place = dynamic_cast(get_custom_viewer_place( 434 | view, 435 | false, // mouse 436 | nullptr, // x 437 | nullptr // y 438 | )); 439 | if (place == nullptr) 440 | { 441 | return false; 442 | } 443 | 444 | auto* token = place->token(); 445 | if (token == nullptr) 446 | { 447 | return false; 448 | } 449 | 450 | func_t* tfnc = nullptr; 451 | if (token->kind == Token::Kind::ID_FNC 452 | && (tfnc = getIdaFunction(token->value))) 453 | { 454 | attach_action_to_popup( 455 | view, 456 | popup, 457 | renameGlobalObj_ah_t::actionName 458 | ); 459 | attach_action_to_popup( 460 | view, 461 | popup, 462 | openXrefs_ah_t::actionName 463 | ); 464 | attach_action_to_popup( 465 | view, 466 | popup, 467 | openCalls_ah_t::actionName 468 | ); 469 | 470 | if (fnc->fnc() == tfnc) 471 | { 472 | attach_action_to_popup( 473 | view, 474 | popup, 475 | changeFuncType_ah_t::actionName 476 | ); 477 | } 478 | 479 | attach_action_to_popup(view, popup, "-"); 480 | } 481 | else if (token->kind == Token::Kind::ID_GVAR) 482 | { 483 | attach_action_to_popup( 484 | view, 485 | popup, 486 | renameGlobalObj_ah_t::actionName 487 | ); 488 | attach_action_to_popup( 489 | view, 490 | popup, 491 | openXrefs_ah_t::actionName 492 | ); 493 | attach_action_to_popup(view, popup, "-"); 494 | } 495 | 496 | attach_action_to_popup( 497 | view, 498 | popup, 499 | jump2asm_ah_t::actionName 500 | ); 501 | attach_action_to_popup( 502 | view, 503 | popup, 504 | copy2asm_ah_t::actionName 505 | ); 506 | attach_action_to_popup( 507 | view, 508 | popup, 509 | funcComment_ah_t::actionName 510 | ); 511 | 512 | break; 513 | } 514 | 515 | 516 | case ui_get_lines_rendering_info: 517 | { 518 | auto* demoSyncGroup = get_synced_group(custViewer); 519 | if (demoSyncGroup == nullptr) 520 | { 521 | return false; 522 | } 523 | 524 | auto* demoPlace = dynamic_cast(get_custom_viewer_place( 525 | custViewer, 526 | false, // mouse 527 | nullptr, // x 528 | nullptr // y 529 | )); 530 | if (demoPlace == nullptr) 531 | { 532 | return false; 533 | } 534 | auto eas = demoPlace->fnc()->yx_2_eas(demoPlace->yx()); 535 | 536 | lines_rendering_output_t* out = va_arg(va, lines_rendering_output_t*); 537 | TWidget* view = va_arg(va, TWidget*); 538 | lines_rendering_input_t* info = va_arg(va, lines_rendering_input_t*); 539 | 540 | if (view == nullptr || info->sync_group != demoSyncGroup) 541 | { 542 | return false; 543 | } 544 | 545 | for (auto& sl : info->sections_lines) 546 | for (auto& l : sl) 547 | { 548 | if (eas.count(l->at->toea())) 549 | { 550 | out->entries.push_back(new line_rendering_output_entry_t( 551 | l, 552 | LROEF_FULL_LINE, 553 | 0xff000000 + 0x90ee90 554 | )); 555 | } 556 | } 557 | 558 | break; 559 | } 560 | 561 | // TWidget is being closed. 562 | case ui_widget_invisible: 563 | { 564 | TWidget* view = va_arg(va, TWidget*); 565 | if (view != custViewer && view != codeViewer) 566 | { 567 | return false; 568 | } 569 | 570 | unhook_event_listener(HT_UI, this); 571 | custViewer = nullptr; 572 | codeViewer = nullptr; 573 | break; 574 | } 575 | } 576 | 577 | return false; 578 | } 579 | 580 | // 581 | //============================================================================== 582 | // cv handlers 583 | //============================================================================== 584 | // 585 | 586 | /** 587 | * Called whenever the user moves the cursor around (mouse, keyboard). 588 | * Fine-tune 'loc->place()' according to the X position. 589 | * 590 | * Without this, retdec_place_t's X position would not change when cursor moves 591 | * around. 592 | * Changing the position triggers some actions. E.g. retdec_place_t::print(). 593 | * 594 | * custom_viewer_adjust_place_t 595 | */ 596 | void idaapi cv_adjust_place(TWidget* v, lochist_entry_t* loc, void* ud) 597 | { 598 | auto* plc = static_cast(loc->place()); 599 | auto* fnc = plc->fnc(); 600 | 601 | retdec_place_t nplc( 602 | fnc, 603 | fnc->adjust_yx(YX( 604 | plc->y(), 605 | loc->renderer_info().pos.cx 606 | ))); 607 | 608 | if (plc->compare(&nplc) != 0) // not equal 609 | { 610 | loc->set_place(nplc); 611 | } 612 | } 613 | 614 | bool idaapi cv_double(TWidget* cv, int shift, void* ud) 615 | { 616 | RetDec* plg = static_cast(ud); 617 | auto* place = dynamic_cast(get_custom_viewer_place( 618 | cv, 619 | false, // mouse 620 | nullptr, // x 621 | nullptr // y 622 | )); 623 | if (place == nullptr) 624 | { 625 | return false; 626 | } 627 | 628 | auto* token = place->token(); 629 | if (token == nullptr || token->kind != Token::Kind::ID_FNC) 630 | { 631 | return false; 632 | } 633 | auto fncName = token->value; 634 | 635 | auto* fnc = plg->getIdaFunction(fncName); 636 | if (fnc == nullptr) 637 | { 638 | INFO_MSG("function \"" << fncName << "\" not found in IDA functions\n"); 639 | return false; 640 | } 641 | 642 | jumpto(fnc->start_ea, -1, UIJMP_ACTIVATE); 643 | 644 | return true; 645 | } 646 | 647 | /** 648 | * custom_viewer_location_changed_t 649 | */ 650 | void idaapi cv_location_changed( 651 | TWidget* v, 652 | const lochist_entry_t* was, 653 | const lochist_entry_t* now, 654 | const locchange_md_t& md, 655 | void* ud) 656 | { 657 | RetDec* ctx = static_cast(ud); 658 | 659 | auto* oldp = dynamic_cast(was->place()); 660 | auto* newp = dynamic_cast(now->place()); 661 | if (oldp->compare(newp) == 0) // equal 662 | { 663 | return; 664 | } 665 | 666 | if (oldp->fnc() != newp->fnc()) 667 | { 668 | retdec_place_t min(newp->fnc(), newp->fnc()->min_yx()); 669 | retdec_place_t max(newp->fnc(), newp->fnc()->max_yx()); 670 | set_custom_viewer_range(ctx->custViewer, &min, &max); 671 | ctx->fnc = newp->fnc(); 672 | } 673 | } 674 | 675 | /** 676 | * custom_viewer_get_place_xcoord_t 677 | */ 678 | int idaapi cv_get_place_xcoord( 679 | TWidget* v, 680 | const place_t* pline, 681 | const place_t* pitem, 682 | void* ud) 683 | { 684 | auto* mpline = static_cast(pline); 685 | auto* mpitem = static_cast(pitem); 686 | 687 | if (mpline->y() != mpitem->y()) 688 | { 689 | return -1; // not included 690 | } 691 | // i.e. mpline->y() == mpitem->y() 692 | else if (mpitem->x() == 0) 693 | { 694 | return -2; // points to entire line 695 | } 696 | else 697 | { 698 | return mpitem->x(); // included at coordinate 699 | } 700 | } 701 | -------------------------------------------------------------------------------- /src/idaplugin/ui.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETDEC_UI_H 3 | #define RETDEC_UI_H 4 | 5 | #include "utils.h" 6 | 7 | class RetDec; 8 | 9 | struct fullDecompilation_ah_t : public action_handler_t 10 | { 11 | inline static const char* actionName = "retdec:ActionFullDecompilation"; 12 | inline static const char* actionLabel = "Create C file RetDec..."; 13 | inline static const char* actionHotkey = "Ctrl+Shift+D"; 14 | 15 | RetDec& plg; 16 | fullDecompilation_ah_t(RetDec& p); 17 | 18 | virtual int idaapi activate(action_activation_ctx_t*) override; 19 | virtual action_state_t idaapi update(action_update_ctx_t*) override; 20 | }; 21 | 22 | struct jump2asm_ah_t : public action_handler_t 23 | { 24 | inline static const char* actionName = "retdec:ActionJump2Asm"; 25 | inline static const char* actionLabel = "Jump to assembly"; 26 | inline static const char* actionHotkey = "A"; 27 | 28 | RetDec& plg; 29 | jump2asm_ah_t(RetDec& p); 30 | 31 | virtual int idaapi activate(action_activation_ctx_t*) override; 32 | virtual action_state_t idaapi update(action_update_ctx_t*) override; 33 | }; 34 | 35 | struct copy2asm_ah_t : public action_handler_t 36 | { 37 | inline static const char* actionName = "retdec:ActionCopy2Asm"; 38 | inline static const char* actionLabel = "Copy to assembly"; 39 | inline static const char* actionHotkey = ""; 40 | 41 | RetDec& plg; 42 | copy2asm_ah_t(RetDec& p); 43 | 44 | virtual int idaapi activate(action_activation_ctx_t*) override; 45 | virtual action_state_t idaapi update(action_update_ctx_t*) override; 46 | }; 47 | 48 | struct funcComment_ah_t : public action_handler_t 49 | { 50 | inline static const char* actionName = "retdec:ActionFunctionComment"; 51 | inline static const char* actionLabel = "Edit func comment"; 52 | inline static const char* actionHotkey = ";"; 53 | 54 | RetDec& plg; 55 | funcComment_ah_t(RetDec& p); 56 | 57 | virtual int idaapi activate(action_activation_ctx_t*) override; 58 | virtual action_state_t idaapi update(action_update_ctx_t*) override; 59 | }; 60 | 61 | struct renameGlobalObj_ah_t : public action_handler_t 62 | { 63 | inline static const char* actionName = "retdec:RenameGlobalObj"; 64 | inline static const char* actionLabel = "Rename global object"; 65 | inline static const char* actionHotkey = "R"; 66 | 67 | RetDec& plg; 68 | renameGlobalObj_ah_t(RetDec& p); 69 | 70 | virtual int idaapi activate(action_activation_ctx_t*) override; 71 | virtual action_state_t idaapi update(action_update_ctx_t*) override; 72 | }; 73 | 74 | struct openXrefs_ah_t : public action_handler_t 75 | { 76 | inline static const char* actionName = "retdec:OpenXrefs"; 77 | inline static const char* actionLabel = "Open xrefs"; 78 | inline static const char* actionHotkey = "X"; 79 | 80 | RetDec& plg; 81 | openXrefs_ah_t(RetDec& p); 82 | 83 | virtual int idaapi activate(action_activation_ctx_t*) override; 84 | virtual action_state_t idaapi update(action_update_ctx_t*) override; 85 | }; 86 | 87 | struct openCalls_ah_t : public action_handler_t 88 | { 89 | inline static const char* actionName = "retdec:OpenCalls"; 90 | inline static const char* actionLabel = "Open calls"; 91 | inline static const char* actionHotkey = "C"; 92 | 93 | RetDec& plg; 94 | openCalls_ah_t(RetDec& p); 95 | 96 | virtual int idaapi activate(action_activation_ctx_t*) override; 97 | virtual action_state_t idaapi update(action_update_ctx_t*) override; 98 | }; 99 | 100 | struct changeFuncType_ah_t : public action_handler_t 101 | { 102 | inline static const char* actionName = "retdec:ChangeFuncType"; 103 | inline static const char* actionLabel = "Change function type"; 104 | inline static const char* actionHotkey = "T"; 105 | 106 | RetDec& plg; 107 | changeFuncType_ah_t(RetDec& p); 108 | 109 | virtual int idaapi activate(action_activation_ctx_t*) override; 110 | virtual action_state_t idaapi update(action_update_ctx_t*) override; 111 | }; 112 | 113 | bool idaapi cv_double(TWidget* cv, int shift, void* ud); 114 | void idaapi cv_adjust_place(TWidget* v, lochist_entry_t* loc, void* ud); 115 | int idaapi cv_get_place_xcoord( 116 | TWidget* v, 117 | const place_t* pline, 118 | const place_t* pitem, 119 | void* ud 120 | ); 121 | void idaapi cv_location_changed( 122 | TWidget *v, 123 | const lochist_entry_t* was, 124 | const lochist_entry_t* now, 125 | const locchange_md_t& md, 126 | void* ud 127 | ); 128 | 129 | static const custom_viewer_handlers_t ui_handlers( 130 | nullptr, // keyboard 131 | nullptr, // popup 132 | nullptr, // mouse_moved 133 | nullptr, // click 134 | cv_double, // dblclick 135 | nullptr, // current position change 136 | nullptr, // close 137 | nullptr, // help 138 | cv_adjust_place, // adjust_place 139 | cv_get_place_xcoord, // get_place_xcoord 140 | cv_location_changed, // location_changed 141 | nullptr // can_navigate 142 | ); 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /src/idaplugin/utils.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | #include "utils.h" 7 | 8 | bool isRelocatable() 9 | { 10 | if (inf_get_filetype() == f_COFF && inf_get_start_ea() == BADADDR) 11 | { 12 | return true; 13 | } 14 | else if (inf_get_filetype() == f_ELF) 15 | { 16 | auto inFile = getInputPath(); 17 | if (inFile.empty()) 18 | { 19 | return false; 20 | } 21 | 22 | std::ifstream infile(inFile, std::ios::binary); 23 | if (infile.good()) 24 | { 25 | std::size_t e_type_offset = 0x10; 26 | infile.seekg(e_type_offset, std::ios::beg); 27 | 28 | // relocatable -- constant 0x1 at <0x10-0x11> 29 | // little endian -- 0x01 0x00 30 | // big endian -- 0x00 0x01 31 | char b1 = 0; 32 | char b2 = 0; 33 | if (infile.get(b1)) 34 | { 35 | if (infile.get(b2)) 36 | { 37 | if (std::size_t(b1) + std::size_t(b2) == 1) 38 | { 39 | return true; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | // f_BIN || f_PE || f_HEX || other 47 | return false; 48 | } 49 | 50 | bool isX86() 51 | { 52 | std::string procName = inf_get_procname().c_str(); 53 | return procName == "80386p" 54 | || procName == "80386r" 55 | || procName == "80486p" 56 | || procName == "80486r" 57 | || procName == "80586p" 58 | || procName == "80586r" 59 | || procName == "80686p" 60 | || procName == "p2" 61 | || procName == "p3" 62 | || procName == "p4" 63 | || procName == "metapc"; 64 | } 65 | 66 | std::string getInputPath() 67 | { 68 | char buff[MAXSTR]; 69 | 70 | get_root_filename(buff, sizeof(buff)); 71 | std::string inName = buff; 72 | 73 | get_input_file_path(buff, sizeof(buff)); 74 | std::string inPath = buff; 75 | 76 | std::string idb = get_path(PATH_TYPE_IDB); 77 | std::string id0 = get_path(PATH_TYPE_ID0); 78 | std::string workDir; 79 | if (!idb.empty()) 80 | { 81 | fs::path fsIdb(idb); 82 | workDir = fsIdb.parent_path().string(); 83 | } 84 | else if (!id0.empty()) 85 | { 86 | fs::path fsId0(id0); 87 | workDir = fsId0.parent_path().string(); 88 | } 89 | if (workDir.empty()) 90 | { 91 | return std::string(); 92 | } 93 | 94 | if (!fs::exists(inPath)) 95 | { 96 | fs::path fsWork(workDir); 97 | fsWork.append(inName); 98 | inPath = fsWork.string(); 99 | 100 | if (!fs::exists(inPath)) 101 | { 102 | char *tmp = ask_file( ///< Returns: file name 103 | false, ///< bool for_saving 104 | nullptr, ///< const char *default_answer 105 | "%s", ///< const char *format 106 | "Input binary to decompile" 107 | ); 108 | 109 | if (tmp == nullptr) 110 | { 111 | return std::string(); 112 | } 113 | if (!fs::exists(std::string(tmp))) 114 | { 115 | return std::string(); 116 | } 117 | 118 | inPath = tmp; 119 | } 120 | } 121 | 122 | return inPath; 123 | } 124 | 125 | void saveIdaDatabase(bool inSitu, const std::string& suffix) 126 | { 127 | INFO_MSG("Saving IDA database ...\n"); 128 | 129 | std::string workIdb = get_path(PATH_TYPE_IDB); 130 | if (workIdb.empty()) 131 | { 132 | return; 133 | } 134 | 135 | auto dotPos = workIdb.find_last_of("."); 136 | if (dotPos != std::string::npos) 137 | { 138 | workIdb.erase(dotPos, std::string::npos); 139 | } 140 | 141 | if (!inSitu) 142 | { 143 | workIdb += suffix; 144 | } 145 | 146 | workIdb += std::string(".") + IDB_EXT; 147 | 148 | save_database(workIdb.c_str(), DBFL_COMP); 149 | 150 | INFO_MSG("IDA database saved into : " << workIdb << "\n"); 151 | } 152 | -------------------------------------------------------------------------------- /src/idaplugin/utils.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETDEC_UTILS_H 3 | #define RETDEC_UTILS_H 4 | 5 | #include 6 | #include 7 | 8 | // IDA SDK includes. 9 | // 10 | #include // this must be included before other idasdk headers 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // General print msg macros. 30 | // 31 | #define PRINT_DEBUG false 32 | #define PRINT_ERROR true 33 | #define PRINT_WARNING true 34 | #define PRINT_INFO true 35 | 36 | #define DBG_MSG(body) \ 37 | if (PRINT_DEBUG) \ 38 | { \ 39 | std::stringstream ss; \ 40 | ss << std::showbase << body; \ 41 | msg("%s", ss.str().c_str()); \ 42 | } 43 | /// Use this only for non-critical error messages. 44 | #define ERROR_MSG(body) \ 45 | if (PRINT_ERROR) \ 46 | { \ 47 | std::stringstream ss; \ 48 | ss << std::showbase << "[RetDec error] :\t" << body; \ 49 | msg("%s", ss.str().c_str()); \ 50 | } 51 | /// Use this only for user info warnings. 52 | #define WARNING_MSG(body) \ 53 | if (PRINT_WARNING) \ 54 | { \ 55 | std::stringstream ss; \ 56 | ss << std::showbase << "[RetDec warning]:\t" << body; \ 57 | msg("%s", ss.str().c_str()); \ 58 | } 59 | /// Use this to inform user. 60 | #define INFO_MSG(body) \ 61 | if (PRINT_INFO) \ 62 | { \ 63 | std::stringstream ss; \ 64 | ss << std::showbase << "[RetDec info] :\t" << body; \ 65 | msg("%s", ss.str().c_str()); \ 66 | } 67 | 68 | /// Use instead of IDA SDK's warning() function. 69 | #define WARNING_GUI(body) \ 70 | { \ 71 | std::stringstream ss; \ 72 | ss << std::showbase << body; \ 73 | warning("%s", ss.str().c_str()); \ 74 | } 75 | 76 | /** 77 | * Is the file currently loaded to IDA relocable? 78 | */ 79 | bool isRelocatable(); 80 | 81 | /** 82 | * Is the file currently loaded to IDA some x86 flavour? 83 | */ 84 | bool isX86(); 85 | 86 | /** 87 | * Get full path to the file currently loaded to IDA. 88 | * Returns empty string if it is unable to get the file. 89 | * May ask user to specify the file in a GUI dialog. 90 | */ 91 | std::string getInputPath(); 92 | 93 | /** 94 | * Save IDA DB before decompilation to protect it if something goes wrong. 95 | * @param inSitu If true, DB is saved with the default IDA name. 96 | * @param suffix If @p inSitu is false, use this suffix to distinguish DBs. 97 | */ 98 | void saveIdaDatabase( 99 | bool inSitu = false, 100 | const std::string &suffix = ".dec-backup" 101 | ); 102 | 103 | #endif -------------------------------------------------------------------------------- /src/idaplugin/yx.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "yx.h" 5 | 6 | const YX YX::starting_yx = YX(YX::starting_y, YX::starting_x); 7 | 8 | YX::YX(std::size_t _y, std::size_t _x) 9 | : y(_y) 10 | , x(_x) 11 | { 12 | 13 | } 14 | 15 | bool YX::operator<(const YX& rhs) const 16 | { 17 | std::pair _this(y, x); 18 | std::pair other(rhs.y, rhs.x); 19 | return _this < other; 20 | } 21 | 22 | bool YX::operator<=(const YX& rhs) const 23 | { 24 | std::pair _this(y, x); 25 | std::pair other(rhs.y, rhs.x); 26 | return _this <= other; 27 | } 28 | 29 | bool YX::operator>(const YX& rhs) const 30 | { 31 | std::pair _this(y, x); 32 | std::pair other(rhs.y, rhs.x); 33 | return _this > other; 34 | } 35 | 36 | bool YX::operator>=(const YX& rhs) const 37 | { 38 | std::pair _this(y, x); 39 | std::pair other(rhs.y, rhs.x); 40 | return _this >= other; 41 | } 42 | 43 | bool YX::operator==(const YX& rhs) const 44 | { 45 | std::pair _this(y, x); 46 | std::pair other(rhs.y, rhs.x); 47 | return _this == other; 48 | } 49 | 50 | std::string YX::toString() const 51 | { 52 | std::stringstream ss; 53 | ss << *this; 54 | return ss.str(); 55 | } 56 | 57 | std::ostream& operator<<(std::ostream& os, const YX& yx) 58 | { 59 | os << "[y=" << std::dec << yx.y << ",x=" << std::dec << yx.x << "]"; 60 | return os; 61 | } 62 | -------------------------------------------------------------------------------- /src/idaplugin/yx.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RETDEC_YX_H 3 | #define RETDEC_YX_H 4 | 5 | #include 6 | #include 7 | 8 | /** 9 | * YX coordinates 10 | * Y = lines 11 | * X = columns 12 | */ 13 | struct YX 14 | { 15 | /// lines 16 | std::size_t y = YX::starting_y; 17 | /// columns 18 | std::size_t x = YX::starting_x; 19 | 20 | inline static const std::size_t starting_y = 1; 21 | inline static const std::size_t starting_x = 0; 22 | static const YX starting_yx; 23 | 24 | YX(std::size_t _y = starting_y, std::size_t _x = starting_x); 25 | 26 | bool operator<(const YX& rhs) const; 27 | bool operator<=(const YX& rhs) const; 28 | bool operator>(const YX& rhs) const; 29 | bool operator>=(const YX& rhs) const; 30 | bool operator==(const YX& rhs) const; 31 | 32 | std::string toString() const; 33 | friend std::ostream& operator<<(std::ostream& os, const YX& yx); 34 | }; 35 | 36 | #endif 37 | --------------------------------------------------------------------------------