├── .appveyor.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── build.sh ├── iatp_autobuild.sh ├── patcher ├── .gitignore ├── CMakeLists.txt ├── ExeController.cpp ├── ExeController.h ├── ExeHandler.cpp ├── ExeHandler.h ├── ExeHandlerLoader.cpp ├── ExeHandlerLoader.h ├── Executables.cpp ├── Executables.h ├── FileLoader.cpp ├── FileLoader.h ├── FuncReplacements.cpp ├── FuncReplacements.h ├── IAT_Patcher.rc ├── ImportsLookup.cpp ├── ImportsLookup.h ├── ImportsTableModel.cpp ├── ImportsTableModel.h ├── InfoTableModel.cpp ├── InfoTableModel.h ├── LICENSE ├── README.md ├── ReplacementsDialog.cpp ├── ReplacementsDialog.h ├── StubMaker.cpp ├── StubMaker.h ├── application.qrc ├── dllparse │ ├── FunctionsModel.cpp │ ├── FunctionsModel.h │ ├── LibraryInfo.cpp │ ├── LibraryInfo.h │ ├── LibraryParser.cpp │ ├── LibraryParser.h │ ├── LibsModel.cpp │ └── LibsModel.h ├── favicon.ico ├── icons │ ├── Add.ico │ ├── Delete.ico │ ├── DeleteAll.ico │ ├── app32.ico │ ├── app64.ico │ ├── apply.ico │ ├── edit.ico │ ├── export.ico │ ├── import.ico │ ├── reload.ico │ └── save_black.ico ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── replacements.ui ├── resource.h └── stub │ ├── Stub.cpp │ ├── Stub.h │ ├── Stub32.cpp │ ├── Stub32.h │ ├── Stub32Data.h │ ├── Stub64.cpp │ ├── Stub64.h │ └── Stub64Data.h └── stub ├── hexf.cpp ├── stub32.asm └── stub64.asm /.appveyor.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - Visual Studio 2015 3 | 4 | platform: x64 5 | - x64 6 | 7 | branches: 8 | only: 9 | - master 10 | 11 | install: 12 | - git submodule update --init --recursive 13 | - set PATH=C:\Program Files\CMake\bin;%PATH% 14 | 15 | build: 16 | verbosity: detailed 17 | 18 | configuration: 19 | - Release 20 | 21 | environment: 22 | artifactName: $(APPVEYOR_PROJECT_NAME)-$(APPVEYOR_REPO_COMMIT)-$(CONFIGURATION) 23 | matrix: 24 | - env_arch: "x64" 25 | QT5: C:\Qt\5.11\msvc2015_64 26 | - env_arch: "x86" 27 | QT5: C:\Qt\5.11\msvc2015 28 | 29 | before_build: 30 | - set PATH=%PATH%;%QT5%\bin;%QT5%\lib\cmake 31 | - set Qt5Core_DIR=%QT5% 32 | - mkdir build 33 | - cd build 34 | - if [%env_arch%]==[x64] ( 35 | cmake .. -A x64 ) 36 | - if [%env_arch%]==[x86] ( 37 | cmake .. ) 38 | 39 | build_script: 40 | - cmake --build . --config %CONFIGURATION% 41 | 42 | after_build: 43 | - mkdir %artifactName% 44 | - cp -r patcher/%CONFIGURATION%/* %artifactName% 45 | - cp %QT5%\bin\Qt5Core.dll %artifactName% 46 | - cp %QT5%\bin\Qt5Gui.dll %artifactName% 47 | - cp %QT5%\bin\Qt5Widgets.dll %artifactName% 48 | - mkdir %artifactName%\platforms 49 | - mkdir %artifactName%\styles 50 | - mkdir %artifactName%\imageformats 51 | - cp %QT5%\plugins\platforms\qwindows.dll %artifactName%\platforms 52 | - cp %QT5%\plugins\styles\qwindowsvistastyle.dll %artifactName%\styles 53 | - cp %QT5%\plugins\imageformats\qico.dll %artifactName%\imageformats 54 | 55 | artifacts: 56 | - path: build\%artifactName% 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bearparser"] 2 | path = bearparser 3 | url = https://github.com/hasherezade/bearparser.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: true 3 | 4 | os: 5 | - linux 6 | 7 | compiler: 8 | - gcc 9 | - clang 10 | 11 | addons: 12 | apt: 13 | packages: 14 | - qt5-default 15 | - qt5-qmake 16 | 17 | before_script: 18 | - cmake --version 19 | - qmake -v 20 | 21 | script: 22 | - mkdir build 23 | - cd build 24 | - mkdir $(pwd)/out 25 | - cmake -DCMAKE_INSTALL_PREFIX:PATH=$(pwd)/out .. 26 | - cmake --build . --target install 27 | 28 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | project (IAT_Patcher) 3 | 4 | # modules: 5 | set ( M_BEARPARSER "bearparser/parser" ) 6 | set ( M_IATP "patcher" ) 7 | 8 | # modules paths: 9 | set (PARSER_DIR "${CMAKE_SOURCE_DIR}/${M_BEARPARSER}" CACHE PATH "BearParser main path") 10 | set (PARSER_INC "${PARSER_DIR}/include" CACHE PATH "BearParser include path") 11 | 12 | set (IATP_DIR "${CMAKE_SOURCE_DIR}/${M_IATP}" CACHE PATH "IATPatcher main path") 13 | 14 | # Add sub-directories 15 | # 16 | # libs 17 | add_subdirectory (${M_BEARPARSER}) 18 | set (PARSER_LIB bearparser CACHE FILE "BearParser library path") 19 | 20 | # executables 21 | add_subdirectory(patcher) 22 | 23 | # dependencies 24 | add_dependencies(IAT_Patcher bearparser) 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016, hasherezade 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | IAT patcher 2 | ========== 3 | 4 | [![Build status](https://ci.appveyor.com/api/projects/status/dv42sbge09b3i77h?svg=true)](https://ci.appveyor.com/project/hasherezade/iat-patcher) 5 | [![Build status](https://travis-ci.org/hasherezade/IAT_patcher.svg?branch=master)](https://travis-ci.org/hasherezade/IAT_patcher) 6 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/e5a1d1892c2642faba08d678c0a6fbf6)](https://www.codacy.com/manual/hasherezade/IAT_patcher?utm_source=github.com&utm_medium=referral&utm_content=hasherezade/IAT_patcher&utm_campaign=Badge_Grade) 7 | [![Total alerts](https://img.shields.io/lgtm/alerts/g/hasherezade/IAT_patcher.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/hasherezade/IAT_patcher/alerts/) 8 | [![GitHub release](https://img.shields.io/github/release/hasherezade/IAT_patcher.svg)](https://github.com/hasherezade/IAT_patcher/releases) 9 | [![Github All Releases](https://img.shields.io/github/downloads/hasherezade/IAT_patcher/total.svg)](https://github.com/hasherezade/IAT_patcher/releases) 10 | 11 | **Persistent IAT hooking application (for PE files).** 12 | 13 | 📖 Read more: http://hasherezade.github.io/IAT_patcher/ 14 | 15 | Please report any bugs and remarks to: [issues](https://github.com/hasherezade/IAT_patcher/issues). 16 | 17 | Requires: 18 | + bearparser: https://github.com/hasherezade/bearparser 19 | + Qt5 SDK 20 | + Qt5 Core 21 | + Qt5 GUI 22 | + cmake http://www.cmake.org 23 | 24 | ## Clone 25 | 26 | Use recursive clone to get the repo together with the submodule: 27 | 28 | ``` 29 | git clone --recursive https://github.com/hasherezade/IAT_patcher.git 30 | ``` 31 | 32 | ## Autobuild 33 | 34 | To build it on Linux or MacOS you can use the given script - it automatically downloads this repository and all the dependencies:
35 | [iatp_autobuild.sh](https://raw.githubusercontent.com/hasherezade/IAT_patcher/master/iatp_autobuild.sh)
36 | Just run it, and it will automatically download and build everything. 37 | 38 | ## Download builds 39 | 40 | You can download the latest stable Windows builds from the [releases](https://github.com/hasherezade/IAT_patcher/releases) 41 | 42 | ## Sample DLLs 43 | 44 | Sample DLLs to be used with IAT Patcher can be found in [IAT_patcher_samples](https://github.com/hasherezade/IAT_patcher_samples) 45 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Trying to autobuild IAT_patcher..." 3 | 4 | #QT check 5 | 6 | QT_VER=`qmake -v` 7 | str=$QT_VER 8 | substr="Qt version 5" 9 | 10 | echo $QT_VER 11 | if [[ $str == *"$substr"* ]]; then 12 | echo "[+] Qt5 found!" 13 | else 14 | str2=`whereis qt5` 15 | substr2="/qt5" 16 | if [[ $str2 == *"$substr2"* ]]; then 17 | echo "[+] Qt5 found!" 18 | else 19 | echo "Install Qt5 SDK first" 20 | exit -1 21 | fi 22 | fi 23 | 24 | CMAKE_VER=`cmake --version` 25 | CMAKEV="cmake version" 26 | if echo "$CMAKE_VER" | grep -q "$CMAKEV"; then 27 | echo "[+] CMake found!" 28 | else 29 | echo "[-] CMake NOT found!" 30 | echo "Install cmake first" 31 | exit -1 32 | fi 33 | 34 | mkdir build 35 | echo "[+] build directory created" 36 | cd build 37 | mkdir $(pwd)/out 38 | cmake -DCMAKE_INSTALL_PREFIX:PATH=$(pwd)/out .. 39 | cmake --build . --target install 40 | 41 | -------------------------------------------------------------------------------- /iatp_autobuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Trying to autobuild IAT_patcher..." 3 | git clone --recursive https://github.com/hasherezade/IAT_patcher.git 4 | echo "[+] IAT_patcher cloned" 5 | echo $$ 6 | cd IAT_patcher 7 | sh build.sh 8 | 9 | -------------------------------------------------------------------------------- /patcher/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /patcher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project (IAT_Patcher) 3 | 4 | message (STATUS "parser_dir='${PARSER_DIR}'") 5 | message (STATUS "parser_lib='${PARSER_LIB}'") 6 | 7 | # Find the QtWidgets library 8 | find_package(Qt5Widgets) 9 | find_package(Qt5Core) 10 | 11 | include_directories ( ${PARSER_INC} ) 12 | 13 | set (stub_hdrs 14 | stub/Stub.h 15 | stub/Stub32.h 16 | stub/Stub64.h 17 | stub/Stub32Data.h 18 | stub/Stub64Data.h 19 | ) 20 | 21 | set (dllparse_hdrs 22 | dllparse/LibraryParser.h 23 | dllparse/LibraryInfo.h 24 | dllparse/LibsModel.h 25 | dllparse/FunctionsModel.h 26 | ) 27 | 28 | set (dllparse_srcs 29 | dllparse/LibraryParser.cpp 30 | dllparse/LibraryInfo.cpp 31 | dllparse/LibsModel.cpp 32 | dllparse/FunctionsModel.cpp 33 | ) 34 | 35 | set (hdrs 36 | StubMaker.h 37 | FuncReplacements.h 38 | ImportsLookup.h 39 | ExeHandler.h 40 | Executables.h 41 | ExeController.h 42 | InfoTableModel.h 43 | ImportsTableModel.h 44 | FileLoader.h 45 | ExeHandlerLoader.h 46 | ReplacementsDialog.h 47 | mainwindow.h 48 | resource.h 49 | ) 50 | 51 | set (stub_srcs 52 | stub/Stub.cpp 53 | stub/Stub32.cpp 54 | stub/Stub64.cpp 55 | ) 56 | 57 | set (srcs 58 | StubMaker.cpp 59 | FuncReplacements.cpp 60 | ImportsLookup.cpp 61 | ExeHandler.cpp 62 | Executables.cpp 63 | ExeController.cpp 64 | InfoTableModel.cpp 65 | ImportsTableModel.cpp 66 | FileLoader.cpp 67 | ExeHandlerLoader.cpp 68 | ReplacementsDialog.cpp 69 | mainwindow.cpp 70 | main.cpp 71 | ) 72 | 73 | set (forms 74 | mainwindow.ui 75 | replacements.ui 76 | ) 77 | 78 | set (rcc application.qrc ) 79 | qt5_add_resources(rcc_src ${rcc}) 80 | 81 | QT5_WRAP_UI (forms_hdrs ${forms}) 82 | QT5_WRAP_CPP (hdrs_moc ${hdrs} ${dllparse_hdrs}) 83 | 84 | SOURCE_GROUP("Source Files\\Auto Generated" FILES ${hdrs_moc} ${rcc_src} ) 85 | SOURCE_GROUP("Source Files\\stub" FILES ${stub_srcs} ) 86 | SOURCE_GROUP("Header Files\\stub" FILES ${stub_hdrs} ) 87 | SOURCE_GROUP("Source Files\\dllparse" FILES ${dllparse_srcs} ) 88 | SOURCE_GROUP("Header Files\\dllparse" FILES ${dllparse_hdrs} ) 89 | 90 | IF (WIN32) 91 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") 92 | ENDIF() 93 | 94 | add_executable (IAT_Patcher IAT_Patcher.rc ${hdrs_moc} ${forms_hdrs} ${stub_srcs} ${stub_hdrs} ${dllparse_hdrs} ${dllparse_srcs} ${hdrs} ${srcs} ${rcc_src}) 95 | 96 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) 97 | 98 | target_link_libraries(IAT_Patcher bearparser Qt5::Core Qt5::Widgets) 99 | 100 | # dependencies 101 | add_dependencies(IAT_Patcher bearparser) 102 | 103 | #install 104 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 105 | 106 | 107 | -------------------------------------------------------------------------------- /patcher/ExeController.cpp: -------------------------------------------------------------------------------- 1 | #include "ExeController.h" 2 | 3 | #include 4 | #include 5 | 6 | bool ExeController::saveExecutable(ExeHandler* exeHndl, QString newFilename) 7 | { 8 | if (exeHndl == NULL) { 9 | return false; 10 | } 11 | 12 | Executable *exe = exeHndl->getExe(); 13 | if (exe == NULL) return false; 14 | 15 | if (AbstractFileBuffer::dump(newFilename, *exe, true) > 0) { 16 | exeHndl->onFileSaved(newFilename); 17 | emit exeUpdated(exeHndl); 18 | return true; 19 | } 20 | return false; 21 | } 22 | 23 | bool ExeController::hookExecutable(ExeHandler* exeHndl, StubSettings &settings) 24 | { 25 | bool isSuccess = StubMaker::makeStub(exeHndl, settings); 26 | //update view even if hooking partialy failed... 27 | StubMaker::fillHookedInfo(exeHndl); 28 | emit exeUpdated(exeHndl); 29 | return isSuccess; 30 | } 31 | 32 | size_t ExeController::saveReplacementsToFile(ExeHandler* exeHndl, QString fileName) 33 | { 34 | if (fileName.length() == 0) { 35 | return 0; 36 | } 37 | size_t counter = exeHndl->m_Repl.save(fileName); 38 | return counter; 39 | } 40 | 41 | size_t ExeController::loadReplacementsFromFile(ExeHandler* exeHndl, QString fileName) 42 | { 43 | if (fileName.length() == 0) return 0; 44 | 45 | size_t counter = exeHndl->m_Repl.load(fileName); 46 | if (counter == 0) { 47 | return 0; 48 | } 49 | 50 | size_t invalidThunks = exeHndl->m_Repl.dropInvalidThunks(exeHndl->m_FuncMap); 51 | counter -= invalidThunks; 52 | if (counter != 0) { 53 | exeHndl->setUnappliedState(true); 54 | } 55 | return counter; 56 | } 57 | -------------------------------------------------------------------------------- /patcher/ExeController.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Executables.h" 5 | #include "StubMaker.h" 6 | 7 | class ExeController : public QObject 8 | { 9 | Q_OBJECT 10 | public: 11 | enum EXE_ACTION 12 | { 13 | ACTION_HOOK = 0, 14 | ACTION_SAVE, 15 | ACTION_UNLOAD, 16 | ACTION_RELOAD, 17 | ACTION_EXPORT_REPL, 18 | ACTION_IMPORT_REPL, 19 | COUNT_ACTION 20 | }; 21 | 22 | ExeController(QObject* parent = NULL) {} 23 | virtual ~ExeController() { } 24 | 25 | bool saveExecutable(ExeHandler* exeHndl, QString newFilename); //throws CustomException 26 | bool hookExecutable(ExeHandler* exeHndl, StubSettings &settings); //throws CustomException 27 | size_t loadReplacementsFromFile(ExeHandler* exeHndl, QString filename); 28 | size_t saveReplacementsToFile(ExeHandler* exeHndl, QString filename); 29 | 30 | signals: 31 | void exeUpdated(ExeHandler* exeHndl); 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /patcher/ExeHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "ExeHandler.h" 2 | 3 | bool ExeHandler::defineReplacement(offset_t thunk, FuncDesc newFunc) 4 | { 5 | bool ret = m_Repl.defineReplacement(thunk, newFunc); 6 | if (ret) hasUnapplied = true; 7 | emit stateChanged(); 8 | return ret; 9 | } 10 | -------------------------------------------------------------------------------- /patcher/ExeHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "ImportsLookup.h" 6 | #include "FuncReplacements.h" 7 | 8 | class ExeHandler : public QObject 9 | { 10 | Q_OBJECT 11 | 12 | signals: 13 | void stateChanged(); 14 | 15 | public: 16 | ExeHandler(AbstractByteBuffer *buf, Executable* exe) 17 | : m_Buf(buf), m_Exe(exe), isModified(false), hasUnapplied(false), 18 | dataStoreRva(INVALID_ADDR) 19 | { 20 | m_FuncMap.wrap(getImports()); 21 | originalEP = exe->getEntryPoint(); 22 | m_fileName = (m_Exe) ? m_Exe->getFileName(): ""; 23 | connect(&m_Repl, SIGNAL(stateChanged()), this, SLOT(onChildStateChanged())); 24 | } 25 | 26 | virtual ~ExeHandler() { delete m_Exe, delete m_Buf;} 27 | Executable* getExe() { return m_Exe; } 28 | 29 | QString getFileName() { return m_fileName; } 30 | 31 | bool defineReplacement(offset_t thunk, FuncDesc newFunc); 32 | FuncDesc getReplAt(offset_t thunk) { return m_Repl.getAt(thunk); } 33 | bool hasReplacements() { return m_Repl.size() > 0; } 34 | 35 | ImportDirWrapper* getImports() 36 | { 37 | MappedExe *mappedExe = dynamic_cast(m_Exe); 38 | if (mappedExe == NULL) return NULL; 39 | 40 | return dynamic_cast(mappedExe->getWrapper(PEFile::WR_DIR_ENTRY + pe::DIR_IMPORT)); 41 | } 42 | 43 | void rewrapFuncMap() 44 | { 45 | this->m_FuncMap.wrap(getImports()); 46 | } 47 | void setHookedState(bool flag) { isHooked = flag; } 48 | bool getHookedState() { return isHooked; } 49 | bool getModifiedState() { return isModified; } 50 | bool getUnappliedState() { return hasUnapplied; } 51 | 52 | offset_t getOriginalEP() { return originalEP; } 53 | offset_t getCurrentEP() { return m_Exe->getEntryPoint(); } 54 | 55 | ImportsLookup m_FuncMap; 56 | FuncReplacements m_Repl; 57 | 58 | public slots: 59 | void onFileSaved(QString newName) 60 | { 61 | this->m_fileName = newName; 62 | this->isModified = false; //modifications are saved 63 | emit stateChanged(); 64 | } 65 | 66 | protected slots: 67 | void onChildStateChanged() { emit stateChanged(); } 68 | 69 | protected: 70 | void setUnappliedState(bool flag) { hasUnapplied = flag; } 71 | 72 | AbstractByteBuffer *m_Buf; 73 | Executable* m_Exe; 74 | 75 | QString m_fileName; 76 | bool isModified, hasUnapplied; 77 | //TODO: finish and refactor it 78 | bool isHooked; 79 | offset_t originalEP, dataStoreRva; // TODO: keep params in separate structure 80 | 81 | friend class StubMaker; 82 | friend class ExeController; 83 | }; 84 | -------------------------------------------------------------------------------- /patcher/ExeHandlerLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "ExeHandlerLoader.h" 2 | 3 | bool ExeHandlerLoader::parse(QString &fileName) 4 | { 5 | bool isLoaded = false; 6 | ExeHandler *exeHndl = NULL; 7 | try { 8 | const bufsize_t MINBUF = 0x200; 9 | AbstractByteBuffer *buf = new FileBuffer(fileName, MINBUF, false); 10 | if (buf == NULL) { 11 | return false; 12 | } 13 | ExeFactory::exe_type exeType = ExeFactory::findMatching(buf); 14 | if (exeType == ExeFactory::NONE) { 15 | delete buf; 16 | return false; 17 | } 18 | 19 | Executable *exe = ExeFactory::build(buf, exeType); 20 | exeHndl = new ExeHandler(buf, exe); 21 | if (exeHndl) { 22 | isLoaded = true; 23 | } 24 | 25 | } catch (CustomException &e) { } 26 | 27 | emit loaded(exeHndl); 28 | return isLoaded; 29 | } 30 | //---- 31 | 32 | -------------------------------------------------------------------------------- /patcher/ExeHandlerLoader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "FileLoader.h" 9 | #include "ExeHandler.h" 10 | 11 | class ExeHandlerLoader : public FileLoader 12 | { 13 | Q_OBJECT 14 | signals: 15 | void loaded(ExeHandler *exeHndl); 16 | 17 | public: 18 | ExeHandlerLoader(QString fileName) : FileLoader(fileName) {} 19 | virtual bool parse(QString &fileName); 20 | }; -------------------------------------------------------------------------------- /patcher/Executables.cpp: -------------------------------------------------------------------------------- 1 | #include "Executables.h" 2 | #include "StubMaker.h" 3 | 4 | size_t Executables::size() { 5 | QMutexLocker lock(&m_listMutex); //LOCKER 6 | return m_Exes.size(); 7 | } 8 | 9 | ExeHandler* Executables::at(size_t index) 10 | { 11 | QMutexLocker lock(&m_listMutex); //LOCKER 12 | if (index >= m_Exes.size()) return NULL; 13 | return m_Exes.at(index); 14 | } 15 | 16 | bool Executables::_addExe(ExeHandler *exeHndl) 17 | { 18 | if (exeHndl == NULL) return false; 19 | 20 | { //start locked scope 21 | QMutexLocker lock(&m_listMutex); //LOCKER 22 | m_Exes.push_back(exeHndl); 23 | } //end locked scope 24 | 25 | StubMaker::fillHookedInfo(exeHndl); 26 | connect(exeHndl, SIGNAL(stateChanged()), this, SLOT(onChildStateChanged())); 27 | return true; 28 | } 29 | 30 | bool Executables::_removeExe(ExeHandler *exe) 31 | { 32 | QMutexLocker lock(&m_listMutex); //LOCKER 33 | size_t indx = m_Exes.indexOf(exe); 34 | if (indx != -1) { 35 | m_Exes.removeAt(indx); 36 | return true; 37 | } 38 | return false; 39 | } 40 | 41 | QStringList Executables::listFiles() 42 | { 43 | QStringList fileNames; 44 | QList::iterator itr; 45 | QString fileName = ""; 46 | 47 | QMutexLocker lock(&m_listMutex); //LOCKER 48 | for (itr = m_Exes.begin(); itr != m_Exes.end(); itr++) { 49 | ExeHandler *exeHndl = (*itr); 50 | if (exeHndl->getExe() != NULL) { 51 | fileName = exeHndl->getFileName(); 52 | fileNames << fileName; 53 | } 54 | } 55 | return fileNames; 56 | } 57 | 58 | void Executables::_clear() 59 | { 60 | QMutexLocker lock(&m_listMutex); //LOCKER 61 | QList::iterator itr; 62 | for (itr = m_Exes.begin(); itr != m_Exes.end(); itr++) { 63 | ExeHandler *exe = (*itr); 64 | delete exe; 65 | } 66 | m_Exes.clear(); 67 | } 68 | -------------------------------------------------------------------------------- /patcher/Executables.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "ExeHandler.h" 6 | 7 | class Executables : public QObject 8 | { 9 | Q_OBJECT 10 | 11 | signals: 12 | void exeListChanged(); 13 | 14 | public slots: 15 | void addExe(ExeHandler *exe) { if (_addExe(exe)) emit exeListChanged(); } 16 | void removeExe(ExeHandler *exe) { if (_removeExe(exe)) emit exeListChanged(); } 17 | void clear() { _clear(); emit exeListChanged(); } 18 | 19 | public: 20 | size_t size(); 21 | ExeHandler* at(size_t index); 22 | 23 | QStringList listFiles(); 24 | 25 | protected slots: 26 | void onChildStateChanged() { emit exeListChanged(); } 27 | 28 | private: 29 | bool _addExe(ExeHandler *exe); 30 | bool _removeExe(ExeHandler *exe); 31 | void _clear(); 32 | 33 | QList m_Exes; 34 | QMutex m_listMutex; 35 | }; 36 | -------------------------------------------------------------------------------- /patcher/FileLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "FileLoader.h" 2 | 3 | #include 4 | 5 | void FileLoader::run() 6 | { 7 | QMutexLocker lock(&m_arrMutex); 8 | bool parsed = false; 9 | try { 10 | if (m_FileName == NULL) return; 11 | parsed = parse(m_FileName); 12 | } catch (...) { } 13 | 14 | if (!parsed) { 15 | emit loadingFailed(m_FileName); 16 | } 17 | emit loaded(m_Buffer); 18 | } 19 | 20 | bool FileLoader::parse(QString &fileName) 21 | { 22 | bufsize_t maxMapSize = FILE_MAXSIZE; 23 | bool isLoaded = false; 24 | try { 25 | const bufsize_t MINBUF = 0x200; 26 | m_Buffer = new FileBuffer(fileName, MINBUF, false); 27 | isLoaded = true; 28 | } catch (CustomException &e) { 29 | } 30 | 31 | return isLoaded; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /patcher/FileLoader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class FileLoader : public QThread 9 | { 10 | Q_OBJECT 11 | public: 12 | FileLoader(QString fileName) : m_Buffer(NULL), m_Status(false) { m_FileName = fileName; } 13 | virtual ~FileLoader() { } 14 | 15 | signals: 16 | void loaded(AbstractByteBuffer *buffer); 17 | void loadingFailed(QString fileName); 18 | 19 | protected: 20 | void run(); 21 | virtual bool parse(QString &fileName); 22 | 23 | QString m_FileName; 24 | bool m_Status; 25 | 26 | 27 | private: 28 | AbstractByteBuffer *m_Buffer; 29 | QMutex m_arrMutex; 30 | }; 31 | -------------------------------------------------------------------------------- /patcher/FuncReplacements.cpp: -------------------------------------------------------------------------------- 1 | #include "FuncReplacements.h" 2 | 3 | bool FuncReplacements::defineReplacement(offset_t thunk, FuncDesc newFunc) 4 | { 5 | if (_defineReplacement(thunk, newFunc)) { 6 | emit stateChanged(); 7 | return true; 8 | } 9 | return false; 10 | } 11 | 12 | bool FuncReplacements::undefReplacement(offset_t thunk) 13 | { 14 | if (_undefReplacement(thunk)) { 15 | emit stateChanged(); 16 | return true; 17 | } 18 | return false; 19 | } 20 | 21 | bool FuncReplacements::_undefReplacement(offset_t thunk) 22 | { 23 | QMap::Iterator found = m_replacements.find(thunk); 24 | bool exist = found != m_replacements.end(); 25 | if (exist) { 26 | m_replacements.erase(found); 27 | return true; 28 | } 29 | return false; 30 | } 31 | 32 | bool FuncReplacements::_defineReplacement(offset_t thunk, FuncDesc newFunc) 33 | { 34 | QMap::Iterator found = m_replacements.find(thunk); 35 | if (newFunc.size() == 0) { 36 | return _undefReplacement(thunk); 37 | } 38 | if (FuncUtil::validateFuncDesc(newFunc) == false) { 39 | return false; 40 | } 41 | m_replacements[thunk] = newFunc; 42 | return true; 43 | } 44 | 45 | FuncDesc FuncReplacements::getAt(offset_t thunk) 46 | { 47 | if (hasAt(thunk) == false) return ""; 48 | return m_replacements[thunk]; 49 | } 50 | 51 | bool FuncReplacements::hasAt(offset_t thunk) 52 | { 53 | return m_replacements.find(thunk) != m_replacements.end(); 54 | } 55 | 56 | size_t FuncReplacements::load(QString &fileName) 57 | { 58 | size_t loaded = 0; 59 | QFile inputFile(fileName); 60 | 61 | if ( !inputFile.open(QIODevice::ReadOnly) ) { 62 | printf("Cannot open file!"); 63 | return 0; 64 | } 65 | offset_t thunk = 0; 66 | QString thunkStr = ""; 67 | QString funcDesc = ""; 68 | 69 | QTextStream in(&inputFile); 70 | while ( !in.atEnd() ) { 71 | QString line = in.readLine(); 72 | QTextStream(&line) >> thunkStr >> funcDesc; 73 | bool isOk = false; 74 | thunk = thunkStr.toLongLong(&isOk, 16); 75 | 76 | if (!isOk || !FuncUtil::validateFuncDesc(funcDesc) ) { 77 | continue; 78 | } 79 | if (_defineReplacement(thunk, funcDesc)) loaded++; 80 | } 81 | inputFile.close(); 82 | 83 | if (loaded > 0) { 84 | emit stateChanged(); 85 | } 86 | return loaded; 87 | } 88 | 89 | size_t FuncReplacements::save(QString &fileName) 90 | { 91 | QFile outputFile(fileName); 92 | if ( !outputFile.open(QIODevice::WriteOnly | QIODevice::Text) ) return 0; 93 | 94 | QTextStream out(&outputFile); 95 | QMap::iterator itr; 96 | size_t counter = 0; 97 | for (itr = m_replacements.begin(); itr != m_replacements.end(); itr++) { 98 | out << hex << itr.key(); 99 | out << " "; 100 | out << itr.value(); 101 | out << '\n'; 102 | counter++; 103 | 104 | } 105 | outputFile.close(); 106 | return counter; 107 | } 108 | 109 | size_t FuncReplacements::dropInvalidThunks(ImportsLookup &lookup) 110 | { 111 | size_t invalidThunks = 0; 112 | QList::iterator itr; 113 | QList thunks = getThunks(); 114 | for (itr = thunks.begin(); itr != thunks.end(); itr++) { 115 | if (lookup.hasThunk(*itr) == false) { 116 | invalidThunks++; 117 | this->_undefReplacement(*itr); 118 | } 119 | } 120 | if (invalidThunks > 0) { 121 | emit stateChanged(); 122 | } 123 | return invalidThunks; 124 | } 125 | -------------------------------------------------------------------------------- /patcher/FuncReplacements.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "ImportsLookup.h" 6 | 7 | typedef QString FuncDesc; 8 | 9 | class FuncUtil { 10 | public: 11 | static bool validateFuncDesc(FuncDesc library) 12 | { 13 | int indx = library.lastIndexOf('.'); 14 | if (indx == -1) { 15 | return false; 16 | } 17 | 18 | QString libName = library.left(indx); 19 | QString funcName = library.mid(indx + 1); 20 | if (libName.length() == 0 || funcName == 0) return false; 21 | return true; 22 | } 23 | 24 | static bool parseFuncDesc(FuncDesc library, QString &libName, QString &funcName) 25 | { 26 | int indx = library.lastIndexOf('.'); 27 | if (indx == -1) { 28 | //printf("Invalid library format!\n"); 29 | return false; 30 | } 31 | 32 | libName = library.left(indx); 33 | funcName = library.mid(indx + 1); 34 | if (libName.length() == 0 || funcName == 0) return false; 35 | return true; 36 | } 37 | }; 38 | 39 | class FuncReplacements : public QObject 40 | { 41 | Q_OBJECT 42 | 43 | signals: 44 | void stateChanged(); 45 | 46 | public: 47 | FuncReplacements() { } 48 | ~FuncReplacements() { } 49 | 50 | size_t load(QString &fileName); 51 | size_t save(QString &fileName); 52 | size_t dropInvalidThunks(ImportsLookup &lookup); 53 | 54 | bool defineReplacement(offset_t thunk, FuncDesc newFunc); 55 | bool undefReplacement(offset_t thunk); 56 | 57 | size_t size() { return m_replacements.size(); } 58 | QList getThunks() { return m_replacements.keys(); } 59 | 60 | FuncDesc getAt(offset_t thunk); 61 | bool hasAt(offset_t thunk); 62 | 63 | protected: 64 | bool _undefReplacement(offset_t thunk); 65 | bool _defineReplacement(offset_t thunk, FuncDesc newFunc); 66 | 67 | QMap m_replacements; 68 | }; 69 | 70 | -------------------------------------------------------------------------------- /patcher/IAT_Patcher.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/IAT_Patcher.rc -------------------------------------------------------------------------------- /patcher/ImportsLookup.cpp: -------------------------------------------------------------------------------- 1 | #include "ImportsLookup.h" 2 | 3 | void ImportsLookup::wrap(ImportDirWrapper* imports) 4 | { 5 | m_Thunks.clear(); 6 | m_Imports = imports; 7 | if (m_Imports != NULL) { 8 | m_Thunks = m_Imports->getThunksList(); 9 | } 10 | wrapFunctions(); 11 | } 12 | 13 | bool ImportsLookup::hasLib(QString libName) 14 | { 15 | libName = libName.toUpper(); 16 | QMap::iterator itr = m_libs.find(libName); 17 | if (itr == m_libs.end()) return false; 18 | return true; 19 | } 20 | 21 | bool ImportsLookup::hasFunc(QString funcName) 22 | { 23 | funcName = funcName.toUpper(); 24 | FuncToThunk::iterator itr = m_func.find(funcName); 25 | if (itr == m_func.end()) return false; 26 | return true; 27 | } 28 | 29 | offset_t ImportsLookup::findThunk(QString libName, QString funcName) 30 | { 31 | libName = libName.toUpper(); 32 | funcName = funcName.toUpper(); 33 | 34 | QMap::iterator itr = m_libs.find(libName); 35 | if (itr == m_libs.end()) return INVALID_ADDR; 36 | 37 | FuncToThunk &func = m_libs[libName]; 38 | FuncToThunk::iterator itr2 = func.find(funcName); 39 | if (itr2 == func.end()) return INVALID_ADDR; 40 | return m_func[funcName]; 41 | } 42 | 43 | /* protected */ 44 | 45 | void ImportsLookup::wrapFunctions() 46 | { 47 | for (size_t i = 0; i < m_Thunks.size(); i++) { 48 | offset_t thunk = m_Thunks[i]; 49 | if (thunk == 0 || thunk == INVALID_ADDR) continue; 50 | 51 | QString lib = thunkToLibName(thunk); 52 | QString func = thunkToFuncName(thunk); 53 | addFunc(lib, func, thunk); 54 | } 55 | } 56 | 57 | bool ImportsLookup::addFunc(QString libName, QString funcName, offset_t thunk) 58 | { 59 | if (libName.size() == 0 || funcName.size() == 0 || thunk == INVALID_ADDR) return false; 60 | 61 | funcName = funcName.toUpper(); 62 | libName = libName.toUpper(); 63 | 64 | m_libs[libName].insert(funcName, thunk); 65 | m_func.insert(funcName, thunk); 66 | return true; 67 | } 68 | -------------------------------------------------------------------------------- /patcher/ImportsLookup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | typedef QMap FuncToThunk; 7 | 8 | class ImportsLookup : public QObject 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | ImportsLookup() { } 14 | 15 | void wrap(ImportDirWrapper* imports); 16 | 17 | QString thunkToLibName(offset_t thunk) { return (m_Imports) ? m_Imports->thunkToLibName(thunk) : ""; } 18 | QString thunkToFuncName(offset_t thunk) { return (m_Imports) ? m_Imports->thunkToFuncName(thunk) : ""; } 19 | 20 | bool hasThunk(const offset_t &thunk) { return m_Thunks.contains(thunk); } 21 | bool hasLib(QString libName); 22 | bool hasFunc(QString funcName); 23 | offset_t findThunk(QString libName, QString funcName); 24 | 25 | size_t countLibs() { return m_libs.size(); } 26 | size_t countImps() { return (m_Imports == NULL) ? 0 : m_Thunks.size(); } 27 | offset_t thunkAt(size_t impNum) { return impNum > m_Thunks.size() ? INVALID_ADDR : m_Thunks.at(impNum); } 28 | 29 | protected: 30 | void wrapFunctions(); 31 | bool addFunc(QString libName, QString funcName, offset_t thunk); 32 | 33 | QMap m_libs; 34 | FuncToThunk m_func; 35 | QList m_Thunks; 36 | 37 | ImportDirWrapper* m_Imports; 38 | }; 39 | -------------------------------------------------------------------------------- /patcher/ImportsTableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "ImportsTableModel.h" 2 | 3 | #include 4 | #include 5 | 6 | QVariant ImportsTableModel::headerData(int section, Qt::Orientation orientation, int role) const 7 | { 8 | if (role != Qt::DisplayRole) return QVariant(); 9 | if (orientation != Qt::Horizontal) return QVariant(); 10 | switch (section) { 11 | case COL_THUNK : return "Thunk"; 12 | case COL_LIB : return "Lib"; 13 | case COL_FUNC : return "Functions"; 14 | case COL_REPLACEMENT : return "ReplacementFunction"; 15 | } 16 | return QVariant(); 17 | } 18 | 19 | Qt::ItemFlags ImportsTableModel::flags(const QModelIndex &index) const 20 | { 21 | if (!index.isValid()) return 0; 22 | Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; 23 | if (index.column() == COL_REPLACEMENT) { 24 | f |= Qt::ItemIsEditable; 25 | } 26 | return f; 27 | } 28 | 29 | bool ImportsTableModel::setData(const QModelIndex &index, const QVariant &data, int role) 30 | { 31 | if (index.column() != COL_REPLACEMENT) { 32 | return false; 33 | } 34 | //TODO: put it in the controller? 35 | offset_t thunk = indexToFunctionThunk(index); 36 | QString substName = data.toString(); 37 | if (this->m_ExeHandler->m_Repl.getAt(thunk) == substName) return false; 38 | 39 | if (this->m_ExeHandler->defineReplacement(thunk, substName) == false) { 40 | QMessageBox::warning(NULL, "Error", "Invalid format supplied!\nValid: ."); 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | QVariant ImportsTableModel::data(const QModelIndex &index, int role) const 47 | { 48 | if (this->countElements() == 0 || m_FuncMap == NULL) return QVariant(); 49 | 50 | offset_t thunk = indexToFunctionThunk(index); 51 | if (thunk == INVALID_ADDR) return QVariant(); 52 | 53 | if (role == Qt::UserRole) { 54 | return qint64(thunk); 55 | } 56 | 57 | if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); 58 | int attribute = index.column(); 59 | switch (attribute) { 60 | case COL_THUNK : return QString::number(thunk, 16); 61 | case COL_LIB : return m_FuncMap->thunkToLibName(thunk); 62 | case COL_FUNC : return m_FuncMap->thunkToFuncName(thunk); 63 | case COL_REPLACEMENT : 64 | { 65 | QString replName = this->m_ExeHandler->getReplAt(thunk); 66 | return replName; 67 | } 68 | } 69 | return QVariant(); 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /patcher/ImportsTableModel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "Executables.h" 6 | 7 | class ImportsTableModel : public QAbstractTableModel 8 | { 9 | Q_OBJECT 10 | 11 | public slots: 12 | void modelChanged() 13 | { 14 | this->beginResetModel(); 15 | this->endResetModel(); 16 | } 17 | 18 | public: 19 | enum COLS 20 | { 21 | COL_THUNK = 0, 22 | COL_LIB, 23 | COL_FUNC, 24 | COL_REPLACEMENT, 25 | COUNT_COL 26 | }; 27 | ImportsTableModel(QObject *v_parent) 28 | : QAbstractTableModel(v_parent), m_ExeHandler(NULL), m_FuncMap(NULL) {} 29 | 30 | virtual ~ImportsTableModel() { } 31 | 32 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 33 | Qt::ItemFlags flags(const QModelIndex &index) const; 34 | 35 | int columnCount(const QModelIndex &parent) const { return COUNT_COL; } 36 | int rowCount(const QModelIndex &parent) const { return countElements(); } 37 | 38 | QVariant data(const QModelIndex &index, int role) const; 39 | bool setData(const QModelIndex &, const QVariant &, int); 40 | 41 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const 42 | { 43 | //no index item pointer 44 | return createIndex(row, column); 45 | } 46 | 47 | QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); } // no parent 48 | 49 | public slots: 50 | void setExecutable(ExeHandler* exeHndl) 51 | { 52 | //> 53 | this->beginResetModel(); 54 | 55 | this->m_FuncMap = NULL; 56 | this->m_ExeHandler = exeHndl; 57 | 58 | if (this->m_ExeHandler) { 59 | this->m_FuncMap = &(m_ExeHandler->m_FuncMap); 60 | 61 | } 62 | this->endResetModel(); 63 | //< 64 | } 65 | 66 | protected slots: 67 | void onExeChanged() 68 | { 69 | //> 70 | this->beginResetModel(); 71 | this->endResetModel(); 72 | //< 73 | } 74 | 75 | protected: 76 | 77 | offset_t indexToFunctionThunk(const QModelIndex &index) const 78 | { 79 | if (index.isValid() == false) { 80 | return INVALID_ADDR; 81 | } 82 | int impNum = index.row(); 83 | size_t entriesCount = countElements(); 84 | if (entriesCount == 0 || impNum >= entriesCount) return INVALID_ADDR; 85 | 86 | offset_t thunk = this->m_FuncMap->thunkAt(impNum); 87 | //printf("Thunk = %llx (%x)\n", thunk, impNum); 88 | return thunk; 89 | } 90 | 91 | ExeHandler* m_ExeHandler; 92 | ImportsLookup *m_FuncMap; 93 | int countElements() const { return (m_FuncMap == NULL) ? 0 : m_FuncMap->countImps(); } 94 | }; 95 | -------------------------------------------------------------------------------- /patcher/InfoTableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "InfoTableModel.h" 2 | #include "StubMaker.h" 3 | 4 | #include 5 | #include 6 | 7 | #define HIGLIHHT_COLOR "yellow" 8 | #define OK_COLOR "green" 9 | #define NOT_OK_COLOR "red" 10 | 11 | QVariant InfoTableModel::headerData(int section, Qt::Orientation orientation, int role) const 12 | { 13 | if (role != Qt::DisplayRole) return QVariant(); 14 | if (orientation != Qt::Horizontal) return QVariant(); 15 | switch (section) { 16 | case COL_NAME : return "FileName"; 17 | case COL_HOOKED: return "Hooked?"; 18 | case COL_RAW_SIZE: return "RawSize"; 19 | case COL_VIRTUAL_SIZE: return "VirtualSize"; 20 | case COL_SECNUM : return "Sections"; 21 | case COL_IMPNUM : return "Imports"; 22 | } 23 | return QVariant(); 24 | } 25 | 26 | Qt::ItemFlags InfoTableModel::flags(const QModelIndex &index) const 27 | { 28 | if (!index.isValid()) return 0; 29 | Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; 30 | 31 | if (index.column() == COL_HOOKED) { 32 | f |= Qt::ItemIsUserCheckable; 33 | f |= Qt::ItemIsEditable; 34 | } 35 | return f; 36 | } 37 | 38 | bool InfoTableModel::setData(const QModelIndex &index, const QVariant &, int role) 39 | { 40 | if (index.column() == COL_HOOKED) { 41 | if ( role == Qt::CheckStateRole) { 42 | int id = index.row(); 43 | if (m_Exes == NULL) return false; 44 | emit hookRequested(this->m_Exes->at(id)); 45 | } 46 | return true; 47 | } 48 | return false; 49 | } 50 | 51 | 52 | QVariant InfoTableModel::data(const QModelIndex &index, int role) const 53 | { 54 | int elNum = index.row(); 55 | if (elNum > countElements()) return QVariant(); 56 | 57 | int attribute = index.column(); 58 | if (attribute >= COUNT_COL) return QVariant(); 59 | 60 | if (role == Qt::UserRole) { 61 | return uint(elNum); 62 | } 63 | 64 | ExeHandler *exeHndl = m_Exes->at(elNum); 65 | if (exeHndl == NULL) return QVariant(); 66 | Executable *exe = exeHndl->getExe(); 67 | if (exe == NULL) return QVariant(); 68 | 69 | if (role == Qt::DisplayRole) { 70 | return getDisplayData(role, attribute, exeHndl); 71 | } 72 | 73 | if (role == Qt::BackgroundColorRole) { 74 | if (exeHndl->getUnappliedState() == true) { 75 | return QColor(HIGLIHHT_COLOR); 76 | } 77 | return QVariant(); 78 | } 79 | 80 | if (role == Qt::DecorationRole && attribute == COL_NAME) { 81 | if (exe->isBit32()) return QIcon(":/icons/app32.ico"); 82 | if (exe->isBit64()) return QIcon(":/icons/app64.ico"); 83 | } 84 | 85 | if (role == Qt::CheckStateRole && attribute == COL_HOOKED) { 86 | if (exeHndl->getHookedState()) return Qt::Checked; 87 | else return Qt::Unchecked; 88 | } 89 | 90 | PEFile *pe = dynamic_cast(exeHndl->getExe()); 91 | if (pe == NULL) return QVariant(); 92 | 93 | if (role == Qt::TextColorRole ) { 94 | if (attribute == COL_SECNUM) { 95 | if (pe->canAddNewSection()) return QColor(OK_COLOR); 96 | return QColor(NOT_OK_COLOR); 97 | } 98 | if (attribute == COL_IMPNUM) { 99 | if (StubMaker::countMissingImports(exeHndl) > 0) return QColor(NOT_OK_COLOR); 100 | return QColor(OK_COLOR); 101 | } 102 | return QVariant(); 103 | } 104 | 105 | if (role == Qt::ToolTipRole ) { 106 | if (attribute == COL_SECNUM) { 107 | if (pe->canAddNewSection()) return "Can add section"; 108 | return "Can NOT add section"; 109 | } 110 | if (attribute == COL_IMPNUM) { 111 | if (StubMaker::countMissingImports(exeHndl) > 0) return "Needs to add imports"; 112 | return "Can reuse all imports"; 113 | } 114 | if (attribute == COL_HOOKED) { 115 | QString info = exeHndl->getHookedState() ? "Hooked": "Not hooked"; 116 | info += "\nDefined "+ QString::number(exeHndl->m_Repl.size()) + " replacements"; 117 | return info; 118 | } 119 | if (attribute == COL_NAME) { 120 | QString epInfo = "OEP\t= "+ QString::number( exeHndl->getOriginalEP(), 16 ); 121 | 122 | if (exeHndl->getHookedState()) { 123 | epInfo += "\nEP \t= "+ QString::number( exeHndl->getCurrentEP(), 16 ); 124 | } 125 | return epInfo; 126 | } 127 | return ""; 128 | } 129 | return QVariant(); 130 | } 131 | 132 | QVariant InfoTableModel::getDisplayData(int role, int attribute, ExeHandler *exeHndl) const 133 | { 134 | Executable *exe = exeHndl->getExe(); 135 | if (exe == NULL) return QVariant(); 136 | 137 | if (role != Qt::DisplayRole) return QVariant(); 138 | 139 | switch (attribute) { 140 | case COL_NAME : 141 | { 142 | QFileInfo inputInfo(exeHndl->getFileName()); 143 | QString name = inputInfo.fileName(); 144 | if (exeHndl->getModifiedState() == true) { 145 | name = "* " + name; 146 | } 147 | return name; 148 | } 149 | case COL_RAW_SIZE : return exe->getMappedSize(Executable::RAW); 150 | case COL_VIRTUAL_SIZE : return exe->getMappedSize(Executable::RVA); 151 | } 152 | 153 | PEFile *pe = dynamic_cast(exeHndl->getExe()); 154 | if (pe == NULL) return QVariant(); 155 | 156 | switch (attribute) { 157 | case COL_SECNUM : 158 | return qint64(pe->hdrSectionsNum()); 159 | 160 | case COL_IMPNUM : 161 | { 162 | ExeNodeWrapper *wr = dynamic_cast(pe->getWrapper(PEFile::WR_DIR_ENTRY + pe::DIR_IMPORT)); 163 | if (!wr) return 0; 164 | return qint64(wr->getEntriesCount()); 165 | } 166 | case COL_HOOKED : 167 | { 168 | return qint64(exeHndl->m_Repl.size()); 169 | } 170 | } 171 | return QVariant(); 172 | } 173 | -------------------------------------------------------------------------------- /patcher/InfoTableModel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Executables.h" 5 | 6 | #include 7 | 8 | class InfoTableModel : public QAbstractTableModel 9 | { 10 | Q_OBJECT 11 | 12 | signals: 13 | void hookRequested(ExeHandler* exe); 14 | 15 | public slots: 16 | void modelChanged() 17 | { 18 | //> 19 | this->beginResetModel(); 20 | this->endResetModel(); 21 | //< 22 | } 23 | 24 | public: 25 | enum COLS 26 | { 27 | COL_NAME = 0, 28 | COL_HOOKED, 29 | COL_RAW_SIZE, 30 | COL_VIRTUAL_SIZE, 31 | COL_SECNUM, 32 | COL_IMPNUM, 33 | COUNT_COL 34 | }; 35 | InfoTableModel(QObject *v_parent) 36 | : QAbstractTableModel(v_parent), m_Exes(NULL) {} 37 | 38 | virtual ~InfoTableModel() { } 39 | 40 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 41 | Qt::ItemFlags flags(const QModelIndex &index) const; 42 | 43 | int columnCount(const QModelIndex &parent) const { return COUNT_COL; } 44 | int rowCount(const QModelIndex &parent) const { return countElements(); } 45 | 46 | QVariant data(const QModelIndex &index, int role) const; 47 | bool setData(const QModelIndex &, const QVariant &, int); 48 | 49 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const 50 | { 51 | //no index item pointer 52 | return createIndex(row, column); 53 | } 54 | 55 | QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); } // no parent 56 | 57 | public slots: 58 | void setExecutables(Executables *exes) 59 | { 60 | //> 61 | this->beginResetModel(); 62 | if (this->m_Exes != NULL) { 63 | //disconnect old 64 | QObject::disconnect(this->m_Exes, SIGNAL(exeListChanged()), this, SLOT( onExeListChanged() ) ); 65 | } 66 | this->m_Exes = exes; 67 | if (this->m_Exes != NULL) { 68 | QObject::connect(this->m_Exes, SIGNAL(exeListChanged()), this, SLOT( onExeListChanged() ) ); 69 | } 70 | this->endResetModel(); 71 | //< 72 | } 73 | protected slots: 74 | void onExeListChanged() 75 | { 76 | //> 77 | this->beginResetModel(); 78 | this->endResetModel(); 79 | //< 80 | } 81 | 82 | protected: 83 | Executables* m_Exes; 84 | 85 | QVariant getDisplayData(int role, int attribute, ExeHandler *exeHndl) const; 86 | int countElements() const { return (m_Exes == NULL)? 0: m_Exes->size(); } 87 | }; 88 | -------------------------------------------------------------------------------- /patcher/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /patcher/README.md: -------------------------------------------------------------------------------- 1 | IAT patcher 2 | ============ 3 | Main application source. 4 | -------------------------------------------------------------------------------- /patcher/ReplacementsDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "ReplacementsDialog.h" 2 | 3 | ReplacementsDialog::ReplacementsDialog(QWidget *parent) 4 | : QDialog(parent) 5 | { 6 | this->m_uiReplacements = new Ui_Replacements(); 7 | this->m_uiReplacements->setupUi(this); 8 | setAcceptDrops(true); 9 | 10 | m_libsModel = new LibsModel(this->m_uiReplacements->libraryCombo); 11 | m_functModel = new FunctionsModel(this->m_uiReplacements->functionCombo); 12 | 13 | this->m_uiReplacements->libraryCombo->setModel(m_libsModel); 14 | this->m_uiReplacements->functionCombo->setModel(m_functModel); 15 | 16 | m_libsModel->setLibraries(&m_libInfos); 17 | m_functModel->setLibraries(&m_libInfos); 18 | 19 | connect(this->m_uiReplacements->okCancel_buttons, SIGNAL(rejected()), this, SLOT(hide())); 20 | connect(this->m_uiReplacements->okCancel_buttons, SIGNAL(accepted()), this, SLOT(requestReplacement())); 21 | connect(m_uiReplacements->addLibraryButton, SIGNAL(clicked()), this, SLOT(openLibrary())); 22 | 23 | connect(m_uiReplacements->libraryCombo, SIGNAL(currentIndexChanged(int)), m_functModel, SLOT(on_currentndexChanged(int))); 24 | connect(this, SIGNAL(parseLibrary(QString&)), &m_libParser, SLOT(on_parseLibrary(QString&)) ); 25 | connect(&m_libParser, SIGNAL(infoCreated(LibraryInfo*)), &m_libInfos, SLOT(addElement(LibraryInfo *)) ); 26 | } 27 | 28 | void ReplacementsDialog::displayFuncToReplace(offset_t thunk, QString libName, QString funcName) 29 | { 30 | this->m_uiReplacements->thunkLabel->setText(QString::number(thunk, 16)); 31 | this->m_uiReplacements->libToReplaceLabel->setText(libName+"."+funcName); 32 | } 33 | 34 | void ReplacementsDialog::displayReplacement(QString libName, QString funcName) 35 | { 36 | this->m_uiReplacements->libraryEdit->setText(libName); 37 | this->m_uiReplacements->functionEdit->setText(funcName); 38 | } 39 | 40 | void ReplacementsDialog::displayReplacement(FuncDesc &desc) 41 | { 42 | QString libName; 43 | QString funcName; 44 | FuncUtil::parseFuncDesc(desc, libName, funcName); 45 | this->displayReplacement(libName, funcName); 46 | } 47 | 48 | QString ReplacementsDialog::getLibName() 49 | { 50 | int tabNum = this->m_uiReplacements->tabWidget->currentIndex(); 51 | 52 | switch (tabNum) { 53 | case TAB_EDIT : 54 | return this->m_uiReplacements->libraryEdit->text(); 55 | case TAB_CHOSE : 56 | return this->m_uiReplacements->libraryCombo->currentText(); 57 | } 58 | return ""; 59 | } 60 | 61 | QString ReplacementsDialog::getFuncName() 62 | { 63 | int tabNum = this->m_uiReplacements->tabWidget->currentIndex(); 64 | 65 | switch (tabNum) { 66 | case TAB_EDIT : 67 | return this->m_uiReplacements->functionEdit->text(); 68 | case TAB_CHOSE : 69 | return this->m_uiReplacements->functionCombo->currentText(); 70 | } 71 | return ""; 72 | } 73 | 74 | void ReplacementsDialog::openLibrary() 75 | { 76 | QString fileName = QFileDialog::getOpenFileName( 77 | this, 78 | tr("Open executable"), 79 | QDir::homePath(), 80 | "DLL Files (*.dll);;All files (*)" 81 | ); 82 | 83 | if (fileName != "") { 84 | emit parseLibrary(fileName); 85 | } 86 | } 87 | 88 | void ReplacementsDialog::requestReplacement() 89 | { 90 | emit setReplacement(getLibName(), getFuncName()); 91 | } 92 | 93 | void ReplacementsDialog::dragEnterEvent(QDragEnterEvent *event) 94 | { 95 | event->accept(); 96 | } 97 | 98 | void ReplacementsDialog::dropEvent(QDropEvent* ev) 99 | { 100 | this->m_uiReplacements->tabWidget->setCurrentIndex(TAB_CHOSE); 101 | QList urls = ev->mimeData()->urls(); 102 | QList::Iterator urlItr; 103 | QCursor cur = this->cursor(); 104 | this->setCursor(Qt::BusyCursor); 105 | 106 | for (urlItr = urls.begin() ; urlItr != urls.end(); urlItr++) { 107 | QString fileName = urlItr->toLocalFile(); 108 | if (fileName == "") continue; 109 | emit parseLibrary(fileName); 110 | } 111 | this->setCursor(cur); 112 | } 113 | -------------------------------------------------------------------------------- /patcher/ReplacementsDialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Executables.h" 8 | #include "ui_replacements.h" 9 | 10 | #include "dllparse/LibraryParser.h" 11 | #include "dllparse/LibsModel.h" 12 | #include "dllparse/FunctionsModel.h" 13 | 14 | class ReplacementsDialog : public QDialog 15 | { 16 | Q_OBJECT 17 | 18 | signals: 19 | void setReplacement(QString libName, QString funcName); 20 | void parseLibrary(QString &path); 21 | 22 | public: 23 | enum TABS 24 | { 25 | TAB_EDIT = 0, 26 | TAB_CHOSE = 1, 27 | COUNT_TABS 28 | }; 29 | 30 | explicit ReplacementsDialog(QWidget *parent = 0); 31 | ~ReplacementsDialog() { } 32 | 33 | void displayFuncToReplace(offset_t thunk, QString libName, QString funcName); 34 | void displayReplacement(QString libName, QString funcName); 35 | void displayReplacement(FuncDesc &desc); 36 | 37 | QString getLibName(); 38 | QString getFuncName(); 39 | 40 | /* events */ 41 | void dragEnterEvent(QDragEnterEvent *ev); 42 | void dropEvent(QDropEvent* ev); 43 | 44 | private slots: 45 | void requestReplacement(); 46 | void openLibrary(); 47 | 48 | private: 49 | Ui_Replacements* m_uiReplacements; 50 | 51 | LibsModel *m_libsModel; 52 | FunctionsModel *m_functModel; 53 | LibInfos m_libInfos; 54 | LibraryParser m_libParser; 55 | }; 56 | -------------------------------------------------------------------------------- /patcher/StubMaker.cpp: -------------------------------------------------------------------------------- 1 | #include "StubMaker.h" 2 | 3 | #include "stub/Stub32.h" 4 | #include "stub/Stub64.h" 5 | 6 | const size_t SEC_PADDING = 10; 7 | 8 | size_t StubMaker::countMissingImports(ImportsLookup &funcMap) 9 | { 10 | QString libName = "Kernel32.dll"; 11 | QStringList funcNames = (QStringList() << "LoadLibraryA" << "GetProcAddress"); // << "VirtualProtect" 12 | 13 | size_t count = 0; 14 | for ( size_t i = 0; i < funcNames.size(); i++) { 15 | offset_t thunk = funcMap.findThunk(libName, funcNames[i]); 16 | if (thunk == INVALID_ADDR) { 17 | count++; 18 | } 19 | } 20 | return count; 21 | } 22 | 23 | bool StubMaker::fillHookedInfo(ExeHandler *exeHndl) 24 | { 25 | if (exeHndl == NULL) return false; 26 | 27 | PEFile *pe = static_cast (exeHndl->getExe()); 28 | if (pe == NULL) return false; 29 | 30 | offset_t ep = pe->getEntryPoint(); 31 | offset_t epRaw = pe->convertAddr(ep, Executable::RVA, Executable::RAW); 32 | if (epRaw == INVALID_ADDR) return false; 33 | 34 | BufferView epView(pe, epRaw, pe->getContentSize() - epRaw); 35 | 36 | Stub *stub = NULL; 37 | if (pe->isBit32()) { 38 | stub = new Stub32(); 39 | } else { 40 | stub = new Stub64(); 41 | } 42 | 43 | bool isContained = stub->containsStub(&epView); 44 | exeHndl->setHookedState(isContained); 45 | 46 | if (isContained) { 47 | if (stub->readParams(&epView)) { 48 | //TODO: fill all the params in exeHndl 49 | /*size_t pCnt = stub->getParamsCount(); 50 | for (size_t id = 0; id< pCnt; id++) { 51 | printf("[%d] = %llx\n", id, stub->getParamValue(id)); 52 | }*/ 53 | //TODO: keep stub params in a separate structure 54 | exeHndl->originalEP = stub->getParamValue(Stub::OEP); 55 | exeHndl->dataStoreRva = stub->getParamValue(Stub::DATA_RVA); 56 | 57 | offset_t dataRva =exeHndl->dataStoreRva; 58 | offset_t dataRaw = pe->toRaw(dataRva, Executable::RVA); 59 | BufferView dataBuf(pe, dataRaw, pe->getContentSize() - dataRaw); 60 | //printf("Reading dataStore at = %llx -> %llx\n", dataRva, dataRaw); 61 | readDataStore(&dataBuf, dataRva, exeHndl->m_Repl); 62 | //printf("Params OK, OEP = %llx\n", exeHndl->originalEP); 63 | } 64 | } 65 | delete stub; 66 | return isContained; 67 | } 68 | 69 | Stub* StubMaker::makeStub(PEFile *pe) 70 | { 71 | if (pe == NULL) return NULL; 72 | 73 | Stub *stb = NULL; 74 | if (pe->isBit32()) { 75 | stb = new Stub32(); 76 | } else { 77 | stb = new Stub64(); 78 | } 79 | return stb; 80 | } 81 | 82 | bool StubMaker::setStubParams(Stub* stb, PEFile *pe, const offset_t newEntry, const offset_t dataRva, ImportsLookup &funcMap) 83 | { 84 | offset_t loadLib = funcMap.findThunk("Kernel32.dll","LoadLibraryA"); 85 | offset_t getProc = funcMap.findThunk("Kernel32.dll","GetProcAddress"); 86 | offset_t oldEntry = pe->getEntryPoint(); 87 | 88 | stb->setParam(Stub::NEW_EP, newEntry); 89 | stb->setParam(Stub::OEP, oldEntry); 90 | stb->setParam(Stub::DATA_RVA, dataRva); 91 | stb->setParam(Stub::FUNC_LOAD_LIB_RVA, loadLib); 92 | stb->setParam(Stub::FUNC_GET_MODULE_RVA, getProc); 93 | 94 | return true; 95 | } 96 | 97 | size_t StubMaker::calcReplNamesSize(FuncReplacements &funcRepl) 98 | { 99 | const size_t PADDING = 1; 100 | size_t requiredLen = PADDING; 101 | 102 | QList thunks = funcRepl.getThunks(); 103 | 104 | QList::Iterator itr; 105 | for (itr = thunks.begin(); itr != thunks.end(); itr++) { 106 | FuncDesc desc = funcRepl.getAt(*itr); 107 | requiredLen += desc.size(); 108 | requiredLen += PADDING; 109 | } 110 | return requiredLen; 111 | } 112 | 113 | size_t StubMaker::calcDataStoreSize(FuncReplacements &funcRepl) 114 | { 115 | const size_t ELEMENTS = 4; //libRVA, funcRVA, thunk, EMPTY 116 | 117 | const size_t OFFSETS_SPACE = (funcRepl.size() * ELEMENTS * sizeof(DWORD)) + sizeof(DWORD) * 2; 118 | const size_t NAMES_SPACE = calcReplNamesSize(funcRepl); 119 | 120 | const size_t TOTAL_SPACE = OFFSETS_SPACE + NAMES_SPACE; 121 | return TOTAL_SPACE; 122 | } 123 | 124 | ByteBuffer* StubMaker::makeDataStore(const offset_t dataRva, FuncReplacements &funcRepl) 125 | { 126 | const size_t ELEMENTS = 4; //libRVA, funcRVA, thunk, EMPTY 127 | 128 | const size_t OFFSETS_SPACE = (funcRepl.size() * ELEMENTS * sizeof(DWORD)) + sizeof(DWORD) * 2; 129 | const size_t NAMES_SPACE = calcReplNamesSize(funcRepl); 130 | 131 | const size_t TOTAL_SPACE = OFFSETS_SPACE + NAMES_SPACE; 132 | char *buffer = new char[TOTAL_SPACE]; 133 | 134 | memset(buffer, 0, TOTAL_SPACE); 135 | size_t DELTA1 = OFFSETS_SPACE; 136 | 137 | char* namesBuf = &buffer[DELTA1]; 138 | 139 | DWORD nameRva = dataRva + DELTA1; 140 | QList allThunks = funcRepl.getThunks(); 141 | 142 | DWORD *fAddress = (DWORD*) buffer; 143 | size_t addrIndx = 0; 144 | 145 | for (int i = 0; i < allThunks.size(); i++) { 146 | 147 | offset_t thunk = allThunks[i]; 148 | FuncDesc library = funcRepl.getAt(thunk); 149 | 150 | QString libName; 151 | QString funcName; 152 | if (FuncUtil::parseFuncDesc(library, libName, funcName) == false) { 153 | //printf("Invalid library format!\n"); 154 | continue; 155 | } 156 | 157 | strcpy(namesBuf, libName.toStdString().c_str()); 158 | fAddress[addrIndx++] = nameRva; 159 | size_t libLen = libName.length() + 1; 160 | nameRva += libLen; 161 | namesBuf += libLen; 162 | 163 | strcpy(namesBuf, funcName.toStdString().c_str()); 164 | fAddress[addrIndx++] = nameRva; 165 | size_t funcLen = funcName.length() + 1; 166 | nameRva += funcLen; 167 | namesBuf += funcLen; 168 | 169 | //save thunk: 170 | fAddress[addrIndx++] = thunk; 171 | 172 | //add space: 173 | fAddress[addrIndx++] = 0; 174 | } 175 | ByteBuffer *dataBuf = new ByteBuffer((BYTE*) buffer, TOTAL_SPACE, 0); 176 | delete []buffer; 177 | return dataBuf; 178 | } 179 | 180 | bool StubMaker::readDataStore(AbstractByteBuffer* buf, const offset_t dataRva, FuncReplacements &funcRepl) 181 | { 182 | const offset_t start = dataRva; 183 | const size_t unitSize = sizeof(DWORD); 184 | 185 | offset_t valOffset = 0; 186 | offset_t value = 0; 187 | 188 | bool isOk = true; 189 | 190 | for (valOffset = 0; isOk ;) { 191 | value = buf->getNumValue(valOffset, unitSize, &isOk); 192 | valOffset += unitSize; 193 | 194 | if (!isOk || value == 0) break; 195 | offset_t dllNameOffset = value - start; 196 | QString dllName = buf->getStringValue(dllNameOffset); 197 | 198 | while (isOk) { 199 | value = buf->getNumValue(valOffset, unitSize, &isOk); 200 | valOffset += unitSize; 201 | offset_t funcNameOffset = value - start; 202 | if (!isOk || value == 0) break; //end of DLL processing 203 | 204 | value = buf->getNumValue(valOffset, unitSize, &isOk); 205 | valOffset += unitSize; 206 | if (!isOk || value == 0) break; //end of DLL processing 207 | offset_t thunk = value; 208 | 209 | QString funcName = buf->getStringValue(funcNameOffset); 210 | //TODO: refactor it... 211 | FuncDesc desc = dllName + "." + funcName; 212 | funcRepl.defineReplacement(thunk, desc); 213 | } 214 | } 215 | return isOk; 216 | } 217 | 218 | bool StubMaker::overwriteDataStore(ExeHandler *exeHndl) 219 | { 220 | if (exeHndl->isHooked == false) return false; 221 | PEFile *pe = dynamic_cast(exeHndl->getExe()); 222 | if (!pe) return false; 223 | 224 | offset_t dataRva = exeHndl->dataStoreRva; 225 | offset_t dataRaw = pe->toRaw(dataRva, Executable::RVA); 226 | if (dataRaw == INVALID_ADDR || dataRaw == 0) return false; 227 | 228 | bufsize_t currentSize = pe->getContentSize() - dataRaw; 229 | bufsize_t requiredSize = calcDataStoreSize(exeHndl->m_Repl); 230 | 231 | if (requiredSize > currentSize) { 232 | bufsize_t dif = requiredSize - currentSize; 233 | //perform resising... 234 | if (!pe->extendLastSection(dif + SEC_PADDING)) return false; 235 | } 236 | 237 | BufferView dataBuf(pe, dataRaw, pe->getContentSize() - dataRaw); 238 | dataBuf.fillContent(0); 239 | ByteBuffer* newStore = makeDataStore(dataRva, exeHndl->m_Repl); 240 | bool isOk = dataBuf.pasteBuffer(0, newStore, false); 241 | delete newStore; 242 | return isOk; 243 | } 244 | 245 | bool StubMaker::addFunction(PEFile *pe, ImportEntryWrapper* libWr, ImportedFuncWrapper* func, const QString& name, offset_t &storageOffset) 246 | { 247 | if (pe == NULL) return false; 248 | 249 | ImportedFuncWrapper* fWr = dynamic_cast(libWr->addEntry(func)); 250 | if (fWr == NULL) { 251 | printf("Cannot add function\n"); 252 | return false; 253 | } 254 | 255 | offset_t thunkRVA = pe->convertAddr(storageOffset, Executable::RAW, Executable::RVA); 256 | 257 | fWr->setNumValue(ImportedFuncWrapper::THUNK, thunkRVA); 258 | fWr->setNumValue(ImportedFuncWrapper::ORIG_THUNK, thunkRVA); 259 | 260 | storageOffset += sizeof(WORD); //add sizeof Hint 261 | if (pe->setStringValue(storageOffset, name) == false) { 262 | printf("Failed to fill lib name\n"); 263 | return false; 264 | } 265 | storageOffset += name.length() + 1; 266 | return true; 267 | } 268 | 269 | ImportEntryWrapper* StubMaker::addLibrary(PEFile *pe, QString name, offset_t &storageOffset) 270 | { 271 | //add new library wrapper: 272 | ImportDirWrapper* imports = dynamic_cast (pe->getWrapper(PEFile::WR_DIR_ENTRY + pe::DIR_IMPORT)); 273 | ImportEntryWrapper* libWr = dynamic_cast (imports->addEntry(NULL)); 274 | if (libWr == NULL) return NULL; 275 | 276 | const size_t PADDING = libWr->getSize(); 277 | storageOffset = imports->getOffset() + imports->getContentSize() + PADDING; 278 | 279 | offset_t nameOffset = storageOffset; 280 | 281 | if (pe->setStringValue(storageOffset, name) == false) { 282 | printf("Failed to fill lib name\n"); 283 | return NULL; 284 | } 285 | storageOffset += name.length() + PADDING; 286 | 287 | offset_t firstThunk = pe->convertAddr(storageOffset, Executable::RAW, Executable::RVA); 288 | 289 | libWr->setNumValue(ImportEntryWrapper::FIRST_THUNK, firstThunk); 290 | libWr->setNumValue(ImportEntryWrapper::ORIG_FIRST_THUNK, firstThunk); 291 | libWr->wrap(); 292 | 293 | storageOffset += PADDING; 294 | offset_t nameRva = pe->convertAddr(nameOffset, Executable::RAW, Executable::RVA); 295 | libWr->setNumValue(ImportEntryWrapper::NAME, nameRva); 296 | 297 | return libWr; 298 | } 299 | 300 | bool StubMaker::addMissingFunctions(PEFile *pe, ImportsLookup &funcMap, bool tryReuse) 301 | { 302 | offset_t storageOffset = 0; 303 | QString library = "Kernel32.dll"; 304 | 305 | ImportEntryWrapper* libWr = addLibrary(pe, library, storageOffset); 306 | if (libWr == NULL) { 307 | //printf("Adding library failed!\n"); 308 | return false; 309 | } 310 | ImportDirWrapper* imports = dynamic_cast (pe->getWrapper(PEFile::WR_DIR_ENTRY + pe::DIR_IMPORT)); 311 | if (imports == NULL) { 312 | printf("Cannot fetch imports!\n"); 313 | return false; 314 | } 315 | 316 | //TODO: add missing without relying on previous... 317 | size_t prevEntry = imports->getEntriesCount() - 2; 318 | ImportEntryWrapper* prevWr = dynamic_cast (imports->getEntryAt(prevEntry)); 319 | if (prevWr == NULL) return false; 320 | 321 | size_t prevWrCount = prevWr->getEntriesCount(); 322 | if (prevWrCount == 0) { 323 | return false; 324 | } 325 | ImportedFuncWrapper* prevLast = dynamic_cast (prevWr->getEntryAt(prevWrCount - 1)); 326 | if (prevLast == NULL) return false; 327 | //---- 328 | const size_t fNum = 2; 329 | const QString fNames[] = {"LoadLibraryA", "GetProcAddress" }; 330 | size_t entrySize = prevLast->getSize(); 331 | 332 | storageOffset += (fNum + 1) * entrySize; 333 | 334 | bool isOk = true; 335 | for ( size_t i = 0; i < fNum; i++) { 336 | 337 | if (tryReuse && funcMap.findThunk(library, fNames[i]) != INVALID_ADDR) { 338 | //Function exist, no need to add... 339 | continue; 340 | } 341 | if (addFunction(pe, libWr, prevLast, fNames[i], storageOffset) == false) { 342 | isOk = false; 343 | break; 344 | } 345 | } 346 | imports->reloadMapping(); 347 | return isOk; 348 | } 349 | 350 | bool StubMaker::makeThunksWriteable(PEFile *pe, FuncReplacements* funcRepl) 351 | { 352 | ImportDirWrapper* imps = dynamic_cast( pe->getDataDirEntry(pe::DIR_IMPORT)); 353 | if (imps == NULL) return false; 354 | 355 | QList thunks = (funcRepl) ? funcRepl->getThunks() : imps->getThunksList(); 356 | //TODO: optimize it 357 | 358 | for (size_t i = 0; i < thunks.size(); i++) { 359 | offset_t thunk = thunks[i]; 360 | SectionHdrWrapper *sHdr = pe->getSecHdrAtOffset(thunk, Executable::RVA, true); 361 | if (sHdr == NULL) continue; 362 | DWORD oldCharact = sHdr->getCharacteristics(); 363 | sHdr->setCharacteristics(oldCharact | 0xC0000000); 364 | } 365 | return true; 366 | } 367 | 368 | size_t StubMaker::calcNewImportsSize(PEFile *pe, size_t addedFuncCount) 369 | { 370 | ImportDirWrapper* imports = dynamic_cast (pe->getWrapper(PEFile::WR_DIR_ENTRY + pe::DIR_IMPORT)); 371 | IMAGE_DATA_DIRECTORY* ddir = pe->getDataDirectory(); 372 | size_t impDirSize = ddir[pe::DIR_IMPORT].Size; 373 | size_t impSize = imports->getContentSize(); 374 | if (impDirSize > impSize) impSize = impDirSize; 375 | 376 | size_t impNewSize = impSize; 377 | impNewSize += sizeof(IMAGE_IMPORT_DESCRIPTOR) * (addedFuncCount + 1); //append new functions + one as padding 378 | return impNewSize; 379 | } 380 | 381 | bool StubMaker::makeStub(PEFile *pe, ImportsLookup &funcMap, FuncReplacements &funcRepl, const StubSettings &settings) 382 | { 383 | try { 384 | if (pe == NULL) { 385 | return false; 386 | } 387 | ExeNodeWrapper* sec = dynamic_cast(pe->getWrapper(PEFile::WR_SECTIONS)); 388 | if (sec == NULL) { 389 | return false; 390 | } 391 | 392 | bufsize_t startOffset = 0; 393 | bufsize_t stubOffset = 0; 394 | bufsize_t TABLE_PADDING = 0x100; 395 | 396 | Stub* stb = StubMaker::makeStub(pe); 397 | 398 | size_t stubSize = stb->getSize(); 399 | bufsize_t stubSectionSize = StubMaker::calcDataStoreSize(funcRepl) + stubSize + TABLE_PADDING; 400 | 401 | bool mustRewriteImports = false; 402 | size_t missingCount = StubMaker::countMissingImports(funcMap); 403 | 404 | //Check if adding imports is required 405 | if ( missingCount != 0 || (settings.reuseImports == false)) { 406 | //TODO: try to add to existing lib... 407 | //TODO: if failed: try to add to existing Import Table... 408 | //if failed: include import dir in the new section size... 409 | mustRewriteImports = true; 410 | 411 | stubOffset = calcNewImportsSize(pe, missingCount) + TABLE_PADDING; 412 | stubSectionSize += stubOffset; 413 | } 414 | 415 | SectionHdrWrapper *stubHdr = NULL; 416 | if (settings.getAddNewSection() && pe->canAddNewSection()) { 417 | stubHdr = pe->addNewSection("stub", stubSectionSize); 418 | if (!stubHdr) { 419 | printf("Failed adding section...\n"); 420 | } 421 | } 422 | 423 | if (stubHdr == NULL) { 424 | stubHdr = pe->getLastSection(); 425 | if (stubHdr == NULL) { 426 | printf("Cannot fetch last section!\n"); 427 | return false; 428 | } 429 | 430 | // resize section 431 | offset_t secROffset = stubHdr->getContentOffset(Executable::RAW, true); 432 | startOffset = pe->getContentSize() - secROffset; 433 | startOffset += SEC_PADDING; 434 | stubHdr = pe->extendLastSection(stubSectionSize + SEC_PADDING); 435 | if (stubHdr == NULL) { 436 | printf("Cannot fetch last section!\n"); 437 | return false; 438 | } 439 | } 440 | DWORD oldCharact = stubHdr->getCharacteristics(); 441 | 442 | stubHdr->setCharacteristics(oldCharact | 0xE0000000); 443 | stubOffset += startOffset; 444 | 445 | //fetch again after modification... 446 | sec = dynamic_cast(pe->getWrapper(PEFile::WR_SECTIONS)); 447 | 448 | if (sec == NULL) { 449 | printf("Cannot fetch sections wrapper\n"); 450 | return false; 451 | } 452 | size_t stubSecId = sec->getEntriesCount() - 1; 453 | //printf("Last section: %d\n", stubSecId); 454 | 455 | const offset_t SEC_RVA = stubHdr->getContentOffset(Executable::RVA); 456 | //printf("Last sectiont RVA = %llx\n", SEC_RVA ); 457 | const offset_t START_RVA = SEC_RVA + startOffset; 458 | //printf("Start writing at : %llx\n", START_RVA ); 459 | 460 | if (mustRewriteImports) { 461 | bool added = false; 462 | try { 463 | if (pe->moveDataDirEntry(pe::DIR_IMPORT, START_RVA, Executable::RVA)) { 464 | added = addMissingFunctions(pe, funcMap, settings.reuseImports); 465 | if (added == false) { 466 | printf("Unable to add all the required functions!\n"); 467 | return false; 468 | } 469 | makeThunksWriteable(pe); 470 | added = true; 471 | } 472 | } catch (ExeException &e) { 473 | printf("Not moved, because: %s\n", e.what()); 474 | } 475 | if (!added) { 476 | printf("Unable to add all the required functions#2\n"); 477 | return false; 478 | } 479 | } 480 | 481 | pe->unbindImports(); 482 | ImportDirWrapper* imps = dynamic_cast( pe->getDataDirEntry(pe::DIR_IMPORT)); 483 | funcMap.wrap(imps); 484 | 485 | BufferView* stubSecView = pe->createSectionView(stubSecId); 486 | if (stubSecView == NULL) { 487 | return false; 488 | } 489 | size_t DATA_OFFSET = stubOffset + stubSize; 490 | offset_t dataRva = SEC_RVA + stubOffset + stubSize; 491 | offset_t newEntryPoint = SEC_RVA + stubOffset; 492 | 493 | ByteBuffer* dataStore = StubMaker::makeDataStore(dataRva, funcRepl); 494 | if (StubMaker::setStubParams(stb, pe, newEntryPoint, dataRva, funcMap) == false) { 495 | printf("Making stub failed: loaderStub == NULL\n"); 496 | } 497 | 498 | ByteBuffer *loaderStub = stb->createStubBuffer(); 499 | delete stb; 500 | stb = NULL; 501 | 502 | bool hasAdded = false; 503 | 504 | if (stubSecView->pasteBuffer(stubOffset, loaderStub, false)) { 505 | pe->setEntryPoint(newEntryPoint, Executable::RVA); 506 | hasAdded = true; 507 | } else { 508 | printf("pasting buffer failed!\n"); 509 | } 510 | 511 | if (hasAdded) { 512 | if (stubSecView->pasteBuffer(DATA_OFFSET, dataStore, false)) { 513 | printf("pasted data"); 514 | hasAdded = true; 515 | } 516 | } 517 | 518 | delete loaderStub; 519 | delete dataStore; 520 | delete stubSecView; 521 | 522 | return hasAdded; 523 | 524 | } catch (CustomException &e) { 525 | printf ("Not added, because: %s\n", e.what()); 526 | } 527 | 528 | return false; 529 | } 530 | -------------------------------------------------------------------------------- /patcher/StubMaker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "Executables.h" 5 | #include "FuncReplacements.h" 6 | #include "stub/Stub.h" 7 | 8 | class StubSettings : public QObject 9 | { 10 | Q_OBJECT 11 | public: 12 | StubSettings() : addNewSection(true), reuseImports(true) {} 13 | void setAddNewSection(bool isEnabled) { this->addNewSection = isEnabled; } 14 | void setReuseImports(bool isEnabled) { this->reuseImports = isEnabled; } 15 | 16 | bool getAddNewSection() const { return this->addNewSection; } 17 | bool getReuseImports() const { return this->reuseImports; } 18 | 19 | protected: 20 | bool addNewSection; 21 | bool reuseImports; 22 | 23 | friend class StubMaker; 24 | }; 25 | 26 | class StubMaker { 27 | 28 | public: 29 | static bool makeStub(ExeHandler *exeHndl, const StubSettings &settings) 30 | { 31 | if (exeHndl == NULL) return false; 32 | bool isOk = false; 33 | PEFile *pe = dynamic_cast(exeHndl->getExe()); 34 | if (exeHndl->isHooked) { 35 | //File already hooked. Overwriting DataStore... 36 | isOk = overwriteDataStore(exeHndl); 37 | } else { 38 | isOk = makeStub(pe, exeHndl->m_FuncMap, exeHndl->m_Repl, settings); 39 | } 40 | if (isOk) { 41 | makeThunksWriteable(pe, &exeHndl->m_Repl); 42 | exeHndl->hasUnapplied = false; 43 | exeHndl->isModified = true; 44 | } 45 | exeHndl->rewrapFuncMap(); 46 | return isOk; 47 | } 48 | 49 | static size_t countMissingImports(ExeHandler *exeHndl) 50 | { 51 | return countMissingImports(exeHndl->m_FuncMap); 52 | } 53 | 54 | static bool fillHookedInfo(ExeHandler *exeHndl); 55 | 56 | protected: 57 | static Stub* makeStub(PEFile *pe); 58 | static bool setStubParams(Stub* stb, PEFile *pe, const offset_t newEntry, const offset_t dataRva, ImportsLookup &funcMap); 59 | 60 | static size_t calcReplNamesSize(FuncReplacements &funcRepl); 61 | static size_t calcDataStoreSize(FuncReplacements &funcRepl); 62 | static size_t calcNewImportsSize(PEFile *pe, size_t addedFuncCount); 63 | 64 | static ByteBuffer* makeDataStore(const offset_t dataRva, FuncReplacements &funcRepl); 65 | static bool readDataStore(AbstractByteBuffer* storeBuf, const offset_t dataRva, FuncReplacements &out_funcRepl); 66 | static bool overwriteDataStore(ExeHandler *exeHndl); 67 | 68 | // static ByteBuffer* createStub32(PEFile *peFile, offset_t stubRva, offset_t loadLib, offset_t getProcAddr); 69 | 70 | static size_t countMissingImports(ImportsLookup &funcMap); 71 | static bool makeThunksWriteable(PEFile *pe, FuncReplacements* m_funcRepl = NULL); 72 | 73 | static bool makeStub(PEFile *pe, ImportsLookup &funcMap, FuncReplacements &funcRepl, const StubSettings &settings); 74 | static bool addMissingFunctions(PEFile *pe, ImportsLookup &funcMap, bool tryReuse); 75 | 76 | static ImportEntryWrapper* addLibrary(PEFile *pe, QString name, offset_t &storageOffset); 77 | static bool addFunction(PEFile *pe, ImportEntryWrapper* libWr, ImportedFuncWrapper* func, const QString& name, offset_t &storageOffset); 78 | 79 | }; 80 | -------------------------------------------------------------------------------- /patcher/application.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/Add.ico 4 | icons/DeleteAll.ico 5 | icons/Delete.ico 6 | icons/reload.ico 7 | icons/app32.ico 8 | icons/app64.ico 9 | icons/apply.ico 10 | icons/save_black.ico 11 | icons/edit.ico 12 | icons/import.ico 13 | icons/export.ico 14 | favicon.ico 15 | 16 | 17 | -------------------------------------------------------------------------------- /patcher/dllparse/FunctionsModel.cpp: -------------------------------------------------------------------------------- 1 | #include "FunctionsModel.h" 2 | 3 | #include 4 | #include 5 | 6 | int FunctionsModel::countElements() const 7 | { 8 | if (m_LibInfos == NULL) return 0; 9 | LibraryInfo *info = m_LibInfos->at(m_libIndex); 10 | if (info == NULL) return 0; 11 | return info->getFunctionsCount(); 12 | } 13 | 14 | QVariant FunctionsModel::headerData(int section, Qt::Orientation orientation, int role) const 15 | { 16 | if (role != Qt::DisplayRole) return QVariant(); 17 | if (orientation != Qt::Horizontal) return QVariant(); 18 | switch (section) { 19 | case COL_NAME : return "Library Name"; 20 | } 21 | return QVariant(); 22 | } 23 | 24 | Qt::ItemFlags FunctionsModel::flags(const QModelIndex &index) const 25 | { 26 | if (!index.isValid()) return Qt::NoItemFlags; 27 | int elNum = index.row(); 28 | LibraryInfo *info = m_LibInfos->at(m_libIndex); 29 | if (info == NULL) return Qt::NoItemFlags; 30 | 31 | if (info->isFunctionNamed(elNum)) 32 | return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 33 | return Qt::NoItemFlags; 34 | } 35 | 36 | bool FunctionsModel::setData(const QModelIndex &index, const QVariant &, int role) 37 | { 38 | return false; 39 | } 40 | 41 | QVariant FunctionsModel::data(const QModelIndex &index, int role) const 42 | { 43 | int elNum = index.row(); 44 | if (elNum > countElements()) return QVariant(); 45 | 46 | int attribute = index.column(); 47 | if (attribute >= COUNT_COL) return QVariant(); 48 | 49 | LibraryInfo *info = m_LibInfos->at(m_libIndex); 50 | if (info == NULL) return QVariant(); 51 | 52 | switch (role) { 53 | case Qt::DisplayRole: return info->getFuncNameAt(elNum); 54 | case Qt::UserRole : return elNum; 55 | case Qt::ToolTipRole: 56 | { 57 | if (!info->isFunctionNamed(elNum)) return "Not supported"; 58 | return ""; 59 | } 60 | } 61 | return QVariant(); 62 | } 63 | -------------------------------------------------------------------------------- /patcher/dllparse/FunctionsModel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "LibraryInfo.h" 7 | 8 | class FunctionsModel : public QAbstractTableModel 9 | { 10 | Q_OBJECT 11 | 12 | public slots: 13 | void modelChanged() 14 | { 15 | //> 16 | this->beginResetModel(); 17 | this->endResetModel(); 18 | //< 19 | } 20 | 21 | void on_currentndexChanged(int index) 22 | { 23 | //> 24 | this->beginResetModel(); 25 | m_libIndex = index; 26 | this->endResetModel(); 27 | //< 28 | } 29 | 30 | public: 31 | enum COLS 32 | { 33 | COL_NAME = 0, 34 | COUNT_COL 35 | }; 36 | 37 | FunctionsModel(QObject *v_parent) 38 | : QAbstractTableModel(v_parent), m_LibInfos(NULL) {} 39 | 40 | virtual ~FunctionsModel() { } 41 | 42 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 43 | Qt::ItemFlags flags(const QModelIndex &index) const; 44 | 45 | int columnCount(const QModelIndex &parent) const { return COUNT_COL; } 46 | int rowCount(const QModelIndex &parent) const { return countElements(); } 47 | 48 | QVariant data(const QModelIndex &index, int role) const; 49 | bool setData(const QModelIndex &, const QVariant &, int); 50 | 51 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const 52 | { 53 | //no index item pointer 54 | return createIndex(row, column); 55 | } 56 | 57 | QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); } // no parent 58 | 59 | public slots: 60 | void setLibraries(LibInfos* exes) 61 | { 62 | //> 63 | this->beginResetModel(); 64 | if (this->m_LibInfos != NULL) { 65 | //disconnect old 66 | QObject::disconnect(this->m_LibInfos, SIGNAL(listChanged()), this, SLOT( on_listChanged() ) ); 67 | } 68 | this->m_LibInfos = exes; 69 | 70 | if (this->m_LibInfos != NULL) { 71 | QObject::connect(this->m_LibInfos, SIGNAL(listChanged()), this, SLOT( on_listChanged() ) ); 72 | } 73 | this->endResetModel(); 74 | //< 75 | } 76 | 77 | protected slots: 78 | void on_listChanged() 79 | { 80 | //> 81 | this->beginResetModel(); 82 | this->endResetModel(); 83 | //< 84 | } 85 | 86 | protected: 87 | int countElements() const; 88 | 89 | LibInfos* m_LibInfos; 90 | size_t m_libIndex; 91 | }; 92 | -------------------------------------------------------------------------------- /patcher/dllparse/LibraryInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "LibraryInfo.h" 2 | 3 | size_t LibInfos::size() { 4 | QMutexLocker lock(&m_listMutex); //LOCKER 5 | return m_Libs.size(); 6 | } 7 | 8 | LibraryInfo* LibInfos::at(size_t index) 9 | { 10 | QMutexLocker lock(&m_listMutex); //LOCKER 11 | if (index >= m_Libs.size()) return NULL; 12 | return m_Libs.at(index); 13 | } 14 | 15 | bool LibInfos::_addElement(LibraryInfo *hndl) 16 | { 17 | if (hndl == NULL) return false; 18 | 19 | { //start locked scope 20 | QMutexLocker lock(&m_listMutex); //LOCKER 21 | const QString fileName = hndl->getFileName(); 22 | if (m_NameToLibInfo.contains(fileName)) { 23 | //already exist, reload... 24 | LibraryInfo *info = m_NameToLibInfo[fileName]; 25 | m_Libs.removeOne(info); 26 | delete info; 27 | } 28 | m_Libs.push_back(hndl); 29 | m_NameToLibInfo[fileName] = hndl; 30 | } //end locked scope 31 | connect(hndl, SIGNAL(stateChanged()), this, SLOT(onChildStateChanged())); 32 | return true; 33 | } 34 | 35 | bool LibInfos::_removeElement(LibraryInfo *exe) 36 | { 37 | QMutexLocker lock(&m_listMutex); //LOCKER 38 | size_t indx = m_Libs.indexOf(exe); 39 | if (indx != -1) { 40 | m_Libs.removeAt(indx); 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | QStringList LibInfos::listLibs() 47 | { 48 | QStringList fileNames; 49 | QList::iterator itr; 50 | QString fileName = ""; 51 | 52 | QMutexLocker lock(&m_listMutex); //LOCKER 53 | for (itr = m_Libs.begin(); itr != m_Libs.end(); itr++) { 54 | LibraryInfo *info = (*itr); 55 | fileNames << info->getLibName(); 56 | } 57 | return fileNames; 58 | } 59 | 60 | void LibInfos::_clear() 61 | { 62 | QMutexLocker lock(&m_listMutex); //LOCKER 63 | QList::iterator itr; 64 | for (itr = m_Libs.begin(); itr != m_Libs.end(); itr++) { 65 | LibraryInfo *exe = (*itr); 66 | delete exe; 67 | } 68 | m_Libs.clear(); 69 | } 70 | -------------------------------------------------------------------------------- /patcher/dllparse/LibraryInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class FunctionInfo 6 | { 7 | public: 8 | FunctionInfo(QString v_name, bool v_isByOrdinal=false) 9 | : name(v_name), isByOrdinal(v_isByOrdinal) {} 10 | virtual ~FunctionInfo() {} 11 | 12 | protected: 13 | QString name; 14 | bool isByOrdinal; 15 | friend class LibraryInfo; 16 | }; 17 | 18 | class LibraryInfo : public QObject 19 | { 20 | Q_OBJECT 21 | 22 | signals: 23 | void stateChanged(); 24 | 25 | public: 26 | LibraryInfo(QString filename, QObject* parent = NULL) 27 | : QObject(parent), fileName(filename) 28 | { 29 | QFileInfo inputInfo(filename); 30 | libName = inputInfo.fileName(); 31 | } 32 | 33 | ~LibraryInfo() {} 34 | 35 | QString getFileName() { return fileName; } 36 | QString getLibName() { return libName; } 37 | const QString getFuncNameAt(int i) { if (i > functions.size()) return ""; return functions.at(i).name; } 38 | bool isFunctionNamed(int i) const { if (i > functions.size()) return false; return !(functions.at(i).isByOrdinal); } 39 | size_t getFunctionsCount() const { return functions.size(); } 40 | 41 | protected: 42 | QString libName; 43 | QString fileName; 44 | QList functions; 45 | 46 | friend class LibraryParser; 47 | }; 48 | 49 | class LibInfos : public QObject 50 | { 51 | Q_OBJECT 52 | 53 | signals: 54 | void listChanged(); 55 | 56 | public slots: 57 | void addElement(LibraryInfo *info) { if (_addElement(info)) emit listChanged(); } 58 | void removeElement(LibraryInfo *info) { if (_removeElement(info)) emit listChanged(); } 59 | void clear() { _clear(); emit listChanged(); } 60 | 61 | public: 62 | size_t size(); 63 | LibraryInfo* at(size_t index); 64 | 65 | QStringList listLibs(); 66 | 67 | protected slots: 68 | void onChildStateChanged() { emit listChanged(); } 69 | 70 | private: 71 | bool _addElement(LibraryInfo *info); 72 | bool _removeElement(LibraryInfo *info); 73 | void _clear(); 74 | 75 | QList m_Libs; 76 | QMap m_NameToLibInfo; 77 | QMutex m_listMutex; 78 | }; 79 | -------------------------------------------------------------------------------- /patcher/dllparse/LibraryParser.cpp: -------------------------------------------------------------------------------- 1 | #include "LibraryParser.h" 2 | 3 | void LibraryParser::on_parseLibrary(QString& fileName) 4 | { 5 | try { 6 | FileView *buf = new FileView(fileName); 7 | 8 | ExeFactory::exe_type exeType = ExeFactory::findMatching(buf); 9 | if (exeType == ExeFactory::NONE) { 10 | delete buf; 11 | return; 12 | } 13 | 14 | Executable *exe = ExeFactory::build(buf, exeType); 15 | makeLibraryInfo(exe, fileName); 16 | delete exe; 17 | delete buf; 18 | } catch (CustomException &e) { 19 | printf("ERR: %s\n", e.what()); 20 | } 21 | } 22 | 23 | ExportDirWrapper* LibraryParser::getExports(Executable* exe) 24 | { 25 | MappedExe *mappedExe = dynamic_cast(exe); 26 | if (mappedExe == NULL) return NULL; 27 | return dynamic_cast(mappedExe->getWrapper(PEFile::WR_DIR_ENTRY + pe::DIR_EXPORT)); 28 | } 29 | 30 | void LibraryParser::makeLibraryInfo(Executable* exe, QString fileName) 31 | { 32 | if (!exe) return; 33 | ExportDirWrapper* exports = getExports(exe); 34 | if (!exports) return; 35 | size_t entriesCnt = exports->getEntriesCount(); 36 | if (entriesCnt == 0) return; 37 | 38 | LibraryInfo *info = new LibraryInfo(fileName); 39 | for(int i = 0; i < entriesCnt; i++) { 40 | ExportEntryWrapper* entry = dynamic_cast(exports->getEntryAt(i)); 41 | if (!entry) continue; 42 | info->functions.append(FunctionInfo(entry->getName(), entry->isByOrdinal())); 43 | } 44 | emit infoCreated(info); 45 | } 46 | -------------------------------------------------------------------------------- /patcher/dllparse/LibraryParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "LibraryInfo.h" 6 | 7 | class LibraryParser: public QObject 8 | { 9 | Q_OBJECT 10 | signals: 11 | void infoCreated(LibraryInfo*); 12 | public: 13 | LibraryParser(QObject* parent = NULL) 14 | : QObject(parent) {} 15 | ~LibraryParser(){} 16 | 17 | void makeLibraryInfo(Executable* exe, QString fileName); 18 | ExportDirWrapper* getExports(Executable* exe); 19 | 20 | public slots: 21 | void on_parseLibrary(QString&); 22 | }; 23 | -------------------------------------------------------------------------------- /patcher/dllparse/LibsModel.cpp: -------------------------------------------------------------------------------- 1 | #include "LibsModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | QVariant LibsModel::headerData(int section, Qt::Orientation orientation, int role) const 8 | { 9 | if (role != Qt::DisplayRole) return QVariant(); 10 | if (orientation != Qt::Horizontal) return QVariant(); 11 | switch (section) { 12 | case COL_NAME : return "Library Name"; 13 | } 14 | return QVariant(); 15 | } 16 | 17 | Qt::ItemFlags LibsModel::flags(const QModelIndex &index) const 18 | { 19 | if (!index.isValid()) return 0; 20 | Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; 21 | return f; 22 | } 23 | 24 | bool LibsModel::setData(const QModelIndex &index, const QVariant &, int role) 25 | { 26 | return false; 27 | } 28 | 29 | QVariant LibsModel::data(const QModelIndex &index, int role) const 30 | { 31 | int elNum = index.row(); 32 | if (elNum > countElements()) return QVariant(); 33 | 34 | int attribute = index.column(); 35 | if (attribute >= COUNT_COL) return QVariant(); 36 | 37 | LibraryInfo *info = m_LibInfos->at(elNum); 38 | if (info == NULL) return QVariant(); 39 | switch (role) { 40 | case Qt::DisplayRole: return info->getLibName(); 41 | case Qt::ToolTipRole: return info->getFileName(); 42 | case Qt::UserRole : return elNum; 43 | } 44 | return QVariant(); 45 | } 46 | -------------------------------------------------------------------------------- /patcher/dllparse/LibsModel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "LibraryInfo.h" 7 | 8 | class LibsModel : public QAbstractTableModel 9 | { 10 | Q_OBJECT 11 | 12 | public slots: 13 | void modelChanged() 14 | { 15 | //> 16 | this->beginResetModel(); 17 | this->endResetModel(); 18 | //< 19 | } 20 | 21 | public: 22 | enum COLS 23 | { 24 | COL_NAME = 0, 25 | COUNT_COL 26 | }; 27 | 28 | LibsModel(QObject *v_parent) 29 | : QAbstractTableModel(v_parent), m_LibInfos(NULL) {} 30 | 31 | virtual ~LibsModel() { } 32 | 33 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 34 | Qt::ItemFlags flags(const QModelIndex &index) const; 35 | 36 | int columnCount(const QModelIndex &parent) const { return COUNT_COL; } 37 | int rowCount(const QModelIndex &parent) const { return countElements(); } 38 | 39 | QVariant data(const QModelIndex &index, int role) const; 40 | bool setData(const QModelIndex &, const QVariant &, int); 41 | 42 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const 43 | { 44 | //no index item pointer 45 | return createIndex(row, column); 46 | } 47 | 48 | QModelIndex parent(const QModelIndex &index) const { return QModelIndex(); } // no parent 49 | 50 | public slots: 51 | void setLibraries(LibInfos* exes) 52 | { 53 | //> 54 | this->beginResetModel(); 55 | 56 | if (this->m_LibInfos != NULL) { 57 | //disconnect old 58 | QObject::disconnect(this->m_LibInfos, SIGNAL(listChanged()), this, SLOT( on_listChanged() ) ); 59 | } 60 | this->m_LibInfos = exes; 61 | 62 | if (this->m_LibInfos != NULL) { 63 | QObject::connect(this->m_LibInfos, SIGNAL(listChanged()), this, SLOT( on_listChanged() ) ); 64 | } 65 | this->endResetModel(); 66 | //< 67 | } 68 | protected slots: 69 | void on_listChanged() 70 | { 71 | //> 72 | this->beginResetModel(); 73 | this->endResetModel(); 74 | //< 75 | } 76 | 77 | protected: 78 | LibInfos* m_LibInfos; 79 | 80 | int countElements() const { return (m_LibInfos == NULL)? 0: m_LibInfos->size(); } 81 | }; 82 | -------------------------------------------------------------------------------- /patcher/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/favicon.ico -------------------------------------------------------------------------------- /patcher/icons/Add.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/Add.ico -------------------------------------------------------------------------------- /patcher/icons/Delete.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/Delete.ico -------------------------------------------------------------------------------- /patcher/icons/DeleteAll.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/DeleteAll.ico -------------------------------------------------------------------------------- /patcher/icons/app32.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/app32.ico -------------------------------------------------------------------------------- /patcher/icons/app64.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/app64.ico -------------------------------------------------------------------------------- /patcher/icons/apply.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/apply.ico -------------------------------------------------------------------------------- /patcher/icons/edit.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/edit.ico -------------------------------------------------------------------------------- /patcher/icons/export.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/export.ico -------------------------------------------------------------------------------- /patcher/icons/import.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/import.ico -------------------------------------------------------------------------------- /patcher/icons/reload.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/reload.ico -------------------------------------------------------------------------------- /patcher/icons/save_black.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/icons/save_black.ico -------------------------------------------------------------------------------- /patcher/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | Q_INIT_RESOURCE(application); 11 | QApplication app(argc, argv); 12 | ExeFactory::init(); 13 | 14 | MainWindow w; 15 | w.show(); 16 | 17 | return app.exec(); 18 | } 19 | -------------------------------------------------------------------------------- /patcher/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ExeHandlerLoader.h" 8 | #include "ImportsTableModel.h" 9 | #include "StubMaker.h" 10 | 11 | MainWindow::MainWindow(QWidget *parent) : 12 | QMainWindow(parent), m_replacementsDialog(NULL), exeController(this), 13 | infoModel(NULL), m_libsModel(NULL), m_functModel(NULL), 14 | m_ExeSelected(NULL), customMenu(NULL), functionsMenu(NULL) 15 | { 16 | m_ui.setupUi(this); 17 | initReplacementsDialog(); 18 | 19 | makeCustomMenu(); 20 | makeFunctionsMenu(); 21 | makeFileMenu(); 22 | this->setWindowTitle("IAT Patcher v " + QString(VERSION) + " Qt" + QString::number(QT_VER_NUM, 10)); 23 | 24 | this->infoModel = new InfoTableModel(m_ui.outputTable); 25 | infoModel->setExecutables(&m_exes); 26 | 27 | m_ui.outputTable->setModel(infoModel); 28 | m_ui.outputTable->setSortingEnabled(false); 29 | m_ui.outputTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); 30 | m_ui.outputTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); 31 | m_ui.outputTable->verticalHeader()->show(); 32 | 33 | m_ui.outputTable->setSelectionBehavior(QAbstractItemView::SelectRows); 34 | m_ui.outputTable->setSelectionMode(QAbstractItemView::SingleSelection); 35 | m_ui.outputTable->setEditTriggers(QAbstractItemView::NoEditTriggers); 36 | 37 | m_ui.statusBar->addPermanentWidget(&urlLabel); 38 | urlLabel.setTextFormat(Qt::RichText); 39 | urlLabel.setTextInteractionFlags(Qt::TextBrowserInteraction); 40 | urlLabel.setOpenExternalLinks(true); 41 | urlLabel.setText(""+SITE_LINK+""); 42 | 43 | this->setAcceptDrops(true); 44 | 45 | this->impModel = new ImportsTableModel(m_ui.outputTable); 46 | this->m_filteredImpModel = new QSortFilterProxyModel(this); 47 | m_filteredImpModel->setDynamicSortFilter(true); 48 | m_filteredImpModel->setSourceModel(impModel); 49 | 50 | m_ui.importsTable->setModel(m_filteredImpModel); 51 | m_ui.importsTable->setSortingEnabled(true); 52 | m_ui.importsTable->setSelectionBehavior(QAbstractItemView::SelectRows); 53 | m_ui.importsTable->setSelectionMode(QAbstractItemView::SingleSelection); 54 | m_ui.importsTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); 55 | 56 | connect( m_ui.filterLibEdit, SIGNAL( textChanged (const QString &)), this, SLOT( filterLibs(const QString &)) ); 57 | connect( m_ui.filterProcEdit, SIGNAL( textChanged (const QString &)), this, SLOT( filterFuncs(const QString &)) ); 58 | m_ui.outputTable->setContextMenuPolicy(Qt::CustomContextMenu); 59 | connect(m_ui.outputTable, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint))); 60 | 61 | m_ui.importsTable->setContextMenuPolicy(Qt::CustomContextMenu); 62 | connect(m_ui.importsTable, SIGNAL(customContextMenuRequested(QPoint)), SLOT(functionsMenuRequested(QPoint))); 63 | 64 | connect( m_ui.outputTable->selectionModel(), SIGNAL( currentRowChanged(QModelIndex,QModelIndex)), this, SLOT( rowChangedSlot(QModelIndex,QModelIndex) ) ); 65 | connect( this, SIGNAL( exeSelected(ExeHandler*)), impModel, SLOT( setExecutable(ExeHandler*) ) ); 66 | connect( this, SIGNAL( exeSelected(ExeHandler*)), this, SLOT( refreshExeView(ExeHandler*) ) ); 67 | 68 | connect( this, SIGNAL( exeUpdated(ExeHandler*)), impModel, SLOT( setExecutable(ExeHandler*) ) ); 69 | connect( this, SIGNAL( exeUpdated(ExeHandler*)), infoModel, SLOT( onExeListChanged() ) ); 70 | connect( &this->exeController, SIGNAL( exeUpdated(ExeHandler*)), this, SLOT( refreshExeView(ExeHandler*)) ); 71 | 72 | connect( &this->exeController, SIGNAL(exeUpdated(ExeHandler*)), infoModel, SLOT(onExeListChanged()) ); 73 | 74 | connect(&m_LoadersCount, SIGNAL(counterChanged()), this, SLOT(loadingStatusChanged() )); 75 | connect(this, SIGNAL(hookRequested(ExeHandler* )), this, SLOT(onHookRequested(ExeHandler* ) )); 76 | connect(infoModel, SIGNAL(hookRequested(ExeHandler* )), this, SLOT(onHookRequested(ExeHandler* )) ); 77 | 78 | connect(this, SIGNAL(thunkSelected(offset_t)), this, SLOT(setThunkSelected(offset_t)) ); 79 | } 80 | 81 | MainWindow::~MainWindow() 82 | { 83 | clear(); 84 | } 85 | 86 | void MainWindow::initReplacementsDialog() 87 | { 88 | m_replacementsDialog = new ReplacementsDialog(this); 89 | connect(m_replacementsDialog, SIGNAL(setReplacement(QString, QString)), this, SLOT(updateReplacement(QString, QString)) ); 90 | connect(this, SIGNAL(replacementAccepted()), m_replacementsDialog, SLOT(hide())); 91 | 92 | } 93 | 94 | void MainWindow::filterLibs(const QString &str) 95 | { 96 | m_filteredImpModel->setFilterRegExp(QRegExp(str, Qt::CaseInsensitive, QRegExp::FixedString)); 97 | m_filteredImpModel->setFilterKeyColumn(1); 98 | } 99 | 100 | void MainWindow::filterFuncs(const QString &str) 101 | { 102 | m_filteredImpModel->setFilterRegExp(QRegExp(str, Qt::CaseInsensitive, QRegExp::FixedString)); 103 | m_filteredImpModel->setFilterKeyColumn(2); 104 | } 105 | 106 | void MainWindow::refreshExeView(ExeHandler* exe) 107 | { 108 | if (m_ExeSelected == exe) { 109 | QString fName = ""; 110 | if (exe) fName = exe->getFileName(); 111 | this->m_ui.fileEdit->setText(fName); 112 | } 113 | } 114 | 115 | void MainWindow::loadingStatusChanged() 116 | { 117 | size_t count = m_LoadersCount.getCount(); 118 | if (count == 0) { 119 | this->m_ui.statusBar->showMessage(""); 120 | return; 121 | } 122 | QString ending = "s"; 123 | if (count == 1) ending = ""; 124 | this->m_ui.statusBar->showMessage("Loading: " + QString::number(count) + " file" + ending); 125 | } 126 | 127 | void MainWindow::dropEvent(QDropEvent* ev) 128 | { 129 | QList urls = ev->mimeData()->urls(); 130 | QList::Iterator urlItr; 131 | QCursor cur = this->cursor(); 132 | this->setCursor(Qt::BusyCursor); 133 | 134 | for (urlItr = urls.begin() ; urlItr != urls.end(); urlItr++) { 135 | QString fileName = urlItr->toLocalFile(); 136 | if (fileName == "") continue; 137 | if (parse(fileName)) { 138 | m_ui.fileEdit->setText(fileName); 139 | } 140 | } 141 | this->setCursor(cur); 142 | } 143 | 144 | void MainWindow::closeEvent ( QCloseEvent * event ) 145 | { 146 | const size_t loadedCount = this->m_exes.size(); 147 | if (loadedCount == 0) { 148 | event->accept(); 149 | return; 150 | } 151 | event->ignore(); 152 | bool hasModified = false; 153 | for (int i = 0; i < loadedCount; i++) { 154 | ExeHandler *hndl = this->m_exes.at(i); 155 | if (hndl->getModifiedState() || hndl->getUnappliedState()) { 156 | hasModified = true; 157 | break; 158 | } 159 | } 160 | if (!hasModified) { 161 | event->accept(); 162 | return; 163 | } 164 | if (QMessageBox::Yes == QMessageBox::question(this, "Exit confirmation", 165 | "You made some unsaved changes, do you really want to exit?", 166 | QMessageBox::Yes|QMessageBox::No)) 167 | { 168 | event->accept(); 169 | } 170 | } 171 | 172 | void MainWindow::on_pushButton_clicked() 173 | { 174 | return openExe(); 175 | } 176 | 177 | void MainWindow::openExe() 178 | { 179 | QString fileName = QFileDialog::getOpenFileName( 180 | this, 181 | tr("Open executable"), 182 | QDir::homePath(), 183 | "Exe Files (*.exe);;DLL Files (*.dll);;All files (*)" 184 | ); 185 | 186 | if (fileName != "") { 187 | this->parse(fileName); 188 | } 189 | } 190 | 191 | void MainWindow::removeExe(ExeHandler* exe) 192 | { 193 | selectExe(NULL); 194 | this->m_exes.removeExe(exe); 195 | delete exe; 196 | exe = NULL; 197 | } 198 | 199 | void MainWindow::on_reloadButton_clicked() 200 | { 201 | QStringList files = this->m_exes.listFiles(); 202 | clear(); 203 | QStringList::iterator itr; 204 | for (itr = files.begin(); itr != files.end(); itr++) { 205 | QString fileName = *itr; 206 | this->parse(fileName); 207 | } 208 | } 209 | 210 | void MainWindow::reloadExe(ExeHandler* exe) 211 | { 212 | QString fName = ""; 213 | if (exe && exe->getExe()) 214 | fName = exe->getFileName(); 215 | this->removeExe(exe); 216 | this->parse(fName); 217 | } 218 | 219 | void MainWindow::saveRequested(ExeHandler* exeHndl) 220 | { 221 | if (exeHndl == NULL) { 222 | QMessageBox::warning(this, "Cannot save!", "No executable selected!"); 223 | return; 224 | } 225 | 226 | Executable *exe = exeHndl->getExe(); 227 | if (exe == NULL) return; 228 | 229 | if (exeHndl->getUnappliedState()) { 230 | QMessageBox::warning(this, "Unapplied replacements!", 231 | "The file has unapplied replacements. Hook the file to apply them."); 232 | return; 233 | 234 | } 235 | if (exeHndl->getHookedState() == false) { 236 | 237 | QMessageBox::StandardButton reply; 238 | reply = QMessageBox::question(this, "Unhooked file!", 239 | "You are trying to save unhooked file. Do you really want?", 240 | QMessageBox::Yes | QMessageBox::No 241 | ); 242 | 243 | if (reply == QMessageBox::No) { 244 | return; 245 | } 246 | } 247 | QFileInfo inputInfo(exeHndl->getFileName()); 248 | 249 | QString infoStr = "Save executable as:"; 250 | QString fileName = QFileDialog::getSaveFileName( 251 | this, 252 | infoStr, 253 | inputInfo.absoluteDir().path(), 254 | "Exe Files (*.exe);;DLL Files (*.dll);;All files (*)" 255 | ); 256 | if (fileName.length() == 0) return; 257 | 258 | try { 259 | this->exeController.saveExecutable(exeHndl, fileName); 260 | } 261 | catch (CustomException &e) { 262 | QMessageBox::warning(this, "Error!", e.getInfo()); 263 | } 264 | } 265 | 266 | void MainWindow::hookExecutable(ExeHandler* exeHndl, StubSettings &settings) 267 | { 268 | if (exeHndl == NULL) return; 269 | 270 | Executable *exe = exeHndl->getExe(); 271 | if (exe == NULL) return; 272 | 273 | bool isHooked = exeHndl->getHookedState(); 274 | PEFile *pe = dynamic_cast(exe); 275 | if (pe == NULL) { 276 | QMessageBox::warning(this, "Cannot hook!", "It is not a PE File!"); 277 | return; 278 | } 279 | if (isHooked) { 280 | if (exeHndl->getUnappliedState() == false) { 281 | QMessageBox::information(NULL, "No changes!", "No changes to be applied"); 282 | return; 283 | } 284 | QMessageBox::StandardButton reply; 285 | reply = QMessageBox::question(this, "Already hooked!", 286 | "This file is already hooked.\nDo you want to modify the existing stub?", 287 | QMessageBox::Yes | QMessageBox::No 288 | ); 289 | 290 | if (reply == QMessageBox::No) { 291 | return; 292 | } 293 | } 294 | 295 | if (exeHndl->hasReplacements() == false && exeHndl->getHookedState() == false) { 296 | QMessageBox::StandardButton reply; 297 | reply = QMessageBox::question(this, "No replacements!", 298 | "You haven't defined any replacement functions.\nDo you really want to add an empty stub?", 299 | QMessageBox::Yes | QMessageBox::No 300 | ); 301 | 302 | if (reply == QMessageBox::No) { 303 | return; 304 | } 305 | } 306 | 307 | if (!isHooked && pe->canAddNewSection() == false && settings.getAddNewSection()) { 308 | QMessageBox::information(this, "Warning", "Cannot add new section in this file!\nProceed by extending last section..."); 309 | } 310 | try { 311 | if (this->exeController.hookExecutable(exeHndl, settings)) { 312 | QMessageBox::information(this, "Done!", "Hooked!\nNow you can save and test the file!"); 313 | return; 314 | } 315 | else { 316 | QMessageBox::warning(this, "Failed", "Cannot hook!"); 317 | } 318 | } 319 | catch (CustomException &e) { 320 | QMessageBox::warning(this, "Error!", e.getInfo()); 321 | } 322 | } 323 | 324 | void MainWindow::clear() 325 | { 326 | selectExe(NULL); 327 | this->m_exes.clear(); 328 | } 329 | 330 | void MainWindow::on_hookButton_clicked() 331 | { 332 | emit hookRequested(this->m_ExeSelected); 333 | } 334 | 335 | QAction* MainWindow::addExeAction(QMenu *customMenu, QString text, ExeController::EXE_ACTION a) 336 | { 337 | QAction *action = new QAction(text, customMenu); 338 | action->setData(a); 339 | customMenu->addAction(action); 340 | connect(action, SIGNAL(triggered()), this, SLOT(takeAction())); 341 | return action; 342 | } 343 | 344 | void MainWindow::makeCustomMenu() 345 | { 346 | this->customMenu = new QMenu(this); 347 | 348 | addExeAction(customMenu, "Hook", ExeController::ACTION_HOOK)->setIcon(QIcon(":/icons/apply.ico")); 349 | addExeAction(customMenu, "Save as...", ExeController::ACTION_SAVE)->setIcon(QIcon(":/icons/save_black.ico")); 350 | addExeAction(customMenu, "Reload", ExeController::ACTION_RELOAD)->setIcon(QIcon(":/icons/reload.ico")); 351 | addExeAction(customMenu, "Unload", ExeController::ACTION_UNLOAD)->setIcon(QIcon(":icons/Delete.ico")); 352 | customMenu->addSeparator(); 353 | addExeAction(customMenu, "Export replacements", ExeController::ACTION_EXPORT_REPL)->setIcon(QIcon(":icons/export.ico")); 354 | addExeAction(customMenu, "Import replacements", ExeController::ACTION_IMPORT_REPL)->setIcon(QIcon(":icons/import.ico")); 355 | } 356 | 357 | void MainWindow::makeFunctionsMenu() 358 | { 359 | this->functionsMenu = new QMenu(this); 360 | 361 | QAction *settingsAction = new QAction("Define replacement", functionsMenu); 362 | settingsAction->setIcon(QIcon(":icons/edit.ico")); 363 | connect(settingsAction, SIGNAL(triggered()), this->m_replacementsDialog, SLOT(show())); 364 | functionsMenu->addAction(settingsAction); 365 | } 366 | 367 | void MainWindow::makeFileMenu() 368 | { 369 | QMenu *menu = this->m_ui.menuFile; 370 | 371 | QAction *openAction = new QAction("Open executable", menu); 372 | openAction->setIcon(QIcon(":/icons/Add.ico")); 373 | connect(openAction, SIGNAL(triggered()), this, SLOT(openExe())); 374 | menu->addAction(openAction); 375 | 376 | addExeAction(menu, "Hook selected", ExeController::ACTION_HOOK)->setIcon(QIcon(":/icons/apply.ico")); 377 | addExeAction(menu, "Save selected as...", ExeController::ACTION_SAVE)->setIcon(QIcon(":/icons/save_black.ico")); 378 | } 379 | 380 | void MainWindow::customMenuRequested(QPoint pos) 381 | { 382 | QTableView *table = this->m_ui.outputTable; 383 | 384 | QModelIndex index = table->indexAt(pos); 385 | if (index.isValid() == false) return; 386 | 387 | this->customMenu->popup(table->viewport()->mapToGlobal(pos)); 388 | } 389 | 390 | void MainWindow::functionsMenuRequested(QPoint pos) 391 | { 392 | QTableView *table = this->m_ui.importsTable; 393 | if (table == NULL ) return; 394 | if (this->m_ExeSelected == NULL) return; 395 | 396 | QModelIndex index = table->indexAt(pos); 397 | if (index.isValid() == false) return; 398 | 399 | bool isOk; 400 | long long offset = m_filteredImpModel->data(index, Qt::UserRole).toLongLong(&isOk); 401 | m_ThunkSelected = isOk ? offset : INVALID_ADDR; 402 | emit thunkSelected(m_ThunkSelected); 403 | 404 | FuncDesc replName = this->m_ExeSelected->getReplAt(m_ThunkSelected); 405 | this->m_replacementsDialog->displayReplacement(replName); 406 | 407 | this->functionsMenu->popup(table->viewport()->mapToGlobal(pos)); 408 | } 409 | 410 | void MainWindow::setThunkSelected(offset_t thunk) 411 | { 412 | QString libName = this->m_ExeSelected->m_FuncMap.thunkToLibName(thunk); 413 | QString funcName = this->m_ExeSelected->m_FuncMap.thunkToFuncName(thunk); 414 | this->m_replacementsDialog->displayFuncToReplace(thunk, libName, funcName); 415 | } 416 | 417 | void MainWindow::updateReplacement(QString libName, QString funcName) 418 | { 419 | QString substName = ""; 420 | 421 | if (libName.length() != 0 && funcName.length() != 0) { 422 | substName = libName + "." + funcName; 423 | } 424 | if (this->m_ExeSelected->m_Repl.getAt(m_ThunkSelected) == substName) { 425 | emit replacementAccepted(); 426 | return; 427 | } 428 | if (this->m_ExeSelected->defineReplacement(m_ThunkSelected, substName) == false) { 429 | QMessageBox::warning(this, "Error", "Invalid replacement definition!"); 430 | return; 431 | } 432 | emit replacementAccepted(); 433 | } 434 | 435 | void MainWindow::onImportReplacements(ExeHandler* exeHndl) 436 | { 437 | if (exeHndl == NULL) return; 438 | 439 | QString infoStr = "Import replacements"; 440 | QFileInfo inputInfo(exeHndl->getFileName()); 441 | 442 | QString fileName = QFileDialog::getOpenFileName( 443 | this, 444 | infoStr, 445 | inputInfo.absoluteDir().path(), 446 | "Config file (*.txt);;Config file (*.cfg);;All files (*)" 447 | ); 448 | if (fileName.length() == 0) return; 449 | 450 | size_t counter = this->exeController.loadReplacementsFromFile(exeHndl, fileName); 451 | if (counter == 0) { 452 | QMessageBox::warning(NULL, "Error!", "Cannot import!"); 453 | return; 454 | } 455 | 456 | if (counter > 0) { 457 | QString ending = (counter > 1) ? "s" : " "; 458 | QMessageBox::information(NULL, "Done!", "Imported: " + QString::number(counter) + " replacement" + ending); 459 | 460 | } 461 | } 462 | 463 | void MainWindow::onExportReplacements(ExeHandler* exeHndl) 464 | { 465 | if (exeHndl == NULL) return; 466 | 467 | if (exeHndl->hasReplacements() == false) { 468 | QMessageBox::warning(NULL, "Cannot save!", "The file have NO replacements defined!"); 469 | return; 470 | } 471 | QString infoStr = "Save replacements as..."; 472 | QFileInfo inputInfo(exeHndl->getFileName()); 473 | 474 | QString fileName = QFileDialog::getSaveFileName( 475 | this, 476 | infoStr, 477 | inputInfo.absoluteDir().path(), 478 | "Config file (*.txt);;Config file (*.cfg);;All files (*)" 479 | ); 480 | if (fileName.length() == 0) return; 481 | 482 | size_t counter = this->exeController.saveReplacementsToFile(exeHndl, fileName); 483 | if (counter == 0) { 484 | QMessageBox::warning(NULL, "Error!", "Cannot export!"); 485 | } 486 | else { 487 | QString ending = (counter > 1) ? "s" : " "; 488 | QMessageBox::information(NULL, "Done!", "Exported: " + QString::number(counter) + " replacement" + ending); 489 | } 490 | } 491 | 492 | void MainWindow::takeAction() 493 | { 494 | QAction *action = qobject_cast(sender()); 495 | //TODO : refactor it 496 | if (action->data() == ExeController::ACTION_HOOK) { 497 | emit hookRequested(this->m_ExeSelected); 498 | return; 499 | } 500 | if (action->data() == ExeController::ACTION_SAVE) { 501 | this->saveRequested(this->m_ExeSelected); 502 | return; 503 | } 504 | if (action->data() == ExeController::ACTION_UNLOAD) { 505 | this->removeExe(this->m_ExeSelected); 506 | return; 507 | } 508 | if (action->data() == ExeController::ACTION_RELOAD) { 509 | this->reloadExe(this->m_ExeSelected); 510 | return; 511 | } 512 | if (action->data() == ExeController::ACTION_IMPORT_REPL) { 513 | this->onImportReplacements(this->m_ExeSelected); 514 | return; 515 | } 516 | if (action->data() == ExeController::ACTION_EXPORT_REPL) { 517 | this->onExportReplacements(this->m_ExeSelected); 518 | return; 519 | } 520 | } 521 | 522 | void MainWindow::onHookRequested(ExeHandler* exeHndl) 523 | { 524 | StubSettings settings; 525 | settings.setAddNewSection(this->m_ui.actionAdd_new_section->isChecked()); 526 | settings.setReuseImports(this->m_ui.actionAdd_imports->isChecked()); 527 | QString settingsStr = "Settings: "; 528 | if (settings.getAddNewSection()) { 529 | settingsStr += "add new section ;"; 530 | } 531 | if (settings.getReuseImports()) { 532 | settingsStr += "reuse imports"; 533 | } 534 | this->m_ui.statusBar->showMessage(settingsStr); 535 | 536 | this->hookExecutable(exeHndl, settings); 537 | } 538 | 539 | void MainWindow::on_saveButton_clicked() 540 | { 541 | saveRequested(this->m_ExeSelected); 542 | } 543 | 544 | void MainWindow::onLoadingFailed(QString fileName) 545 | { 546 | QMessageBox::warning(this, "Error!", "Cannot load the file: " + fileName); 547 | } 548 | 549 | void MainWindow::onLoaderThreadFinished() 550 | { 551 | delete QObject::sender(); 552 | m_LoadersCount.dec(); 553 | this->repaint(); 554 | } 555 | 556 | void MainWindow::rowChangedSlot(QModelIndex curr, QModelIndex prev) 557 | { 558 | bool isOk = false; 559 | size_t index = this->infoModel->data(curr, Qt::UserRole).toUInt(&isOk); 560 | if (!isOk) return; 561 | ExeHandler *exe = this->m_exes.at(index); 562 | selectExe(exe); 563 | } 564 | 565 | bool MainWindow::parse(QString &fileName) 566 | { 567 | if (fileName == "") return false; 568 | 569 | QString link = QFile::symLinkTarget(fileName); 570 | if (link.length() > 0) fileName = link; 571 | 572 | bufsize_t maxMapSize = FILE_MAXSIZE; 573 | try { 574 | FileView fileView(fileName, maxMapSize); 575 | ExeFactory::exe_type exeType = ExeFactory::findMatching(&fileView); 576 | if (exeType == ExeFactory::NONE) { 577 | QMessageBox::warning(this, "Cannot parse!", "Cannot parse the file: \n"+fileName+"\n\nType not supported."); 578 | return false; 579 | } 580 | 581 | ExeHandlerLoader *loader = new ExeHandlerLoader(fileName); 582 | QObject::connect(loader, SIGNAL( loaded(ExeHandler*) ), &m_exes, SLOT( addExe(ExeHandler*) ) ); 583 | QObject::connect(loader, SIGNAL( loadingFailed(QString ) ), this, SLOT( onLoadingFailed(QString ) ) ); 584 | QObject::connect(loader, SIGNAL(finished()), this, SLOT( onLoaderThreadFinished() ) ); 585 | //printf("Thread started...\n"); 586 | m_LoadersCount.inc(); 587 | loader->start(); 588 | 589 | } catch (CustomException &e) { 590 | QMessageBox::warning(this, "ERROR", e.getInfo()); 591 | return false; 592 | } 593 | return true; 594 | } 595 | 596 | void MainWindow::info() 597 | { 598 | int ret = 0; 599 | int count = 0; 600 | QPixmap p(":/favicon.ico"); 601 | QString msg = "IAT Patcher - tool for persistent IAT hooking
"; 602 | msg += "author: hasherezade
"; 603 | msg += "using: Qt" + QString::number(QT_VER_NUM, 10) + "

"; 604 | 605 | msg += "LICENSE: " + LICENSE_TYPE + "
"; 606 | msg += "Sourcecode & more info
"; 607 | msg += "Report an issue"; 608 | 609 | QMessageBox *msgBox = new QMessageBox(this); 610 | msgBox->setAttribute(Qt::WA_DeleteOnClose); 611 | 612 | msgBox->setWindowTitle("Info"); 613 | msgBox->setTextFormat(Qt::RichText); 614 | 615 | msgBox->setText(msg); 616 | msgBox->setAutoFillBackground(true); 617 | msgBox->setIconPixmap(p); 618 | 619 | msgBox->setStandardButtons(QMessageBox::Ok); 620 | msgBox->exec(); 621 | } 622 | -------------------------------------------------------------------------------- /patcher/mainwindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Executables.h" 7 | 8 | #include "ui_mainwindow.h" 9 | #include "ui_replacements.h" 10 | 11 | #include "ImportsTableModel.h" 12 | #include "InfoTableModel.h" 13 | #include "ExeController.h" 14 | #include "ReplacementsDialog.h" 15 | 16 | #ifndef QT_VERSION_MAJOR 17 | #define QT_VER_NUM 5 18 | #else 19 | #define QT_VER_NUM QT_VERSION_MAJOR 20 | #endif 21 | 22 | #define VERSION "0.3.5.4" 23 | #define MY_SITE_LINK "https://hasherezade.net/" 24 | #define LICENSE_TYPE "BSD-2" 25 | #define LICENSE_LINK "https://raw.githubusercontent.com/hasherezade/IAT_patcher/master/LICENSE" 26 | #define SITE_LINK "http://hasherezade.github.io/IAT_patcher/" 27 | #define ISSUES_LINK "https://github.com/hasherezade/IAT_patcher/issues" 28 | 29 | class ThreadCounter : public QObject 30 | { 31 | Q_OBJECT 32 | 33 | signals: 34 | void counterChanged(); 35 | 36 | public: 37 | ThreadCounter() { counter = 0; } 38 | 39 | void inc() 40 | { 41 | { 42 | QMutexLocker lock(&m_Mutex); 43 | counter++; 44 | } 45 | emit counterChanged(); 46 | } 47 | 48 | void dec() 49 | { 50 | { 51 | QMutexLocker lock(&m_Mutex); 52 | counter--; 53 | } 54 | emit counterChanged(); 55 | } 56 | size_t getCount() 57 | { 58 | QMutexLocker lock(&m_Mutex); 59 | return counter; 60 | } 61 | 62 | protected: 63 | size_t counter; 64 | QMutex m_Mutex; 65 | }; 66 | 67 | class MainWindow : public QMainWindow 68 | { 69 | Q_OBJECT 70 | 71 | signals: 72 | void exeSelected(ExeHandler* exe); 73 | void exeUpdated(ExeHandler* exe); 74 | void hookRequested(ExeHandler* exe); 75 | 76 | void thunkSelected(offset_t thunk); 77 | void replacementAccepted(); 78 | 79 | public: 80 | explicit MainWindow(QWidget *parent = 0); 81 | ~MainWindow(); 82 | 83 | protected: 84 | /* events */ 85 | void dragEnterEvent(QDragEnterEvent* ev) { ev->accept(); } 86 | void dropEvent(QDropEvent* ev); 87 | void closeEvent(QCloseEvent* ev); 88 | 89 | private slots: 90 | void filterLibs(const QString &str); 91 | void filterFuncs(const QString &str); 92 | 93 | void takeAction(); 94 | void loadingStatusChanged(); 95 | void onLoadingFailed(QString fileName); 96 | 97 | void selectExe(ExeHandler* exe) 98 | { 99 | m_ExeSelected = exe; 100 | emit exeSelected(exe); 101 | } 102 | 103 | void refreshExeView(ExeHandler* exe); 104 | 105 | void customMenuRequested(QPoint pos); 106 | void functionsMenuRequested(QPoint pos); 107 | void onHookRequested(ExeHandler* exeHndl); 108 | void updateReplacement(QString libName, QString funcName); 109 | void onExportReplacements(ExeHandler* exeHndl); 110 | void onImportReplacements(ExeHandler* exeHndl); 111 | 112 | void setThunkSelected(offset_t thunk); 113 | 114 | void onLoaderThreadFinished(); 115 | void rowChangedSlot(QModelIndex, QModelIndex); 116 | void openExe(); 117 | //--- 118 | void on_pushButton_clicked(); 119 | void on_reloadButton_clicked(); 120 | void on_clearAllButton_clicked() { clear(); } 121 | void on_hookButton_clicked(); 122 | void on_saveButton_clicked(); 123 | 124 | void on_actionAbout_triggered() { info(); } 125 | 126 | private: 127 | void reloadExe(ExeHandler* exe); 128 | void removeExe(ExeHandler* exe); 129 | void saveRequested(ExeHandler* exeHndl); 130 | void hookExecutable(ExeHandler* exeHndl, StubSettings &settings); 131 | void info(); 132 | void clear(); 133 | bool parse(QString &fileName); 134 | 135 | QAction* addExeAction(QMenu *customMenu, QString text, ExeController::EXE_ACTION a); 136 | void makeCustomMenu(); 137 | void makeFunctionsMenu(); 138 | void makeFileMenu(); 139 | void initReplacementsDialog(); 140 | 141 | ReplacementsDialog *m_replacementsDialog; 142 | QMenu *customMenu, *functionsMenu; 143 | 144 | QSortFilterProxyModel *m_filteredImpModel; 145 | 146 | Ui::MainWindow m_ui; 147 | 148 | ImportsTableModel *impModel; 149 | InfoTableModel *infoModel; 150 | LibsModel *m_libsModel; 151 | FunctionsModel *m_functModel; 152 | 153 | ThreadCounter m_LoadersCount; 154 | 155 | Executables m_exes; 156 | ExeHandler* m_ExeSelected; 157 | offset_t m_ThunkSelected; 158 | QLabel urlLabel; 159 | 160 | ExeController exeController; 161 | }; 162 | 163 | 164 | -------------------------------------------------------------------------------- /patcher/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | hasherezade 4 | MainWindow 5 | 6 | 7 | Qt::ApplicationModal 8 | 9 | 10 | 11 | 0 12 | 0 13 | 731 14 | 522 15 | 16 | 17 | 18 | 19 | 100 20 | 300 21 | 22 | 23 | 24 | Qt::StrongFocus 25 | 26 | 27 | IAT Patcher 28 | 29 | 30 | 31 | :/favicon.ico:/favicon.ico 32 | 33 | 34 | 1.000000000000000 35 | 36 | 37 | Qt::LeftToRight 38 | 39 | 40 | true 41 | 42 | 43 | QTabWidget::Rounded 44 | 45 | 46 | 47 | 48 | 49 | 50 | QLayout::SetMinAndMaxSize 51 | 52 | 53 | 54 | 55 | 56 | 57 | true 58 | 59 | 60 | Selected executable 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 0 69 | 0 70 | 71 | 72 | 73 | Open file 74 | 75 | 76 | 77 | 78 | 79 | 80 | :/icons/Add.ico:/icons/Add.ico 81 | 82 | 83 | 84 | 24 85 | 24 86 | 87 | 88 | 89 | true 90 | 91 | 92 | false 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 0 101 | 0 102 | 103 | 104 | 105 | Reload all 106 | 107 | 108 | 109 | 110 | 111 | 112 | :/icons/reload.ico:/icons/reload.ico 113 | 114 | 115 | 116 | 24 117 | 24 118 | 119 | 120 | 121 | true 122 | 123 | 124 | false 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 0 133 | 0 134 | 135 | 136 | 137 | Unload all 138 | 139 | 140 | 141 | 142 | 143 | 144 | :/icons/DeleteAll.ico:/icons/DeleteAll.ico 145 | 146 | 147 | 148 | 24 149 | 24 150 | 151 | 152 | 153 | true 154 | 155 | 156 | false 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | Preview: 166 | 167 | 168 | 169 | 170 | 171 | 172 | Qt::Vertical 173 | 174 | 175 | 176 | true 177 | 178 | 179 | 180 | 0 181 | 0 182 | 183 | 184 | 185 | true 186 | 187 | 188 | QFrame::StyledPanel 189 | 190 | 191 | true 192 | 193 | 194 | true 195 | 196 | 197 | true 198 | 199 | 200 | 201 | 202 | 203 | 0 204 | 0 205 | 206 | 207 | 208 | 209 | 0 210 | 40 211 | 212 | 213 | 214 | 215 | 16777215 216 | 38 217 | 218 | 219 | 220 | 221 | 1 222 | 223 | 224 | 1 225 | 226 | 227 | 228 | 229 | 230 | 0 231 | 0 232 | 233 | 234 | 235 | false 236 | 237 | 238 | false 239 | 240 | 241 | Filter libraries 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 0 250 | 0 251 | 252 | 253 | 254 | false 255 | 256 | 257 | false 258 | 259 | 260 | Filter functions 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 0 270 | 0 271 | 272 | 273 | 274 | Qt::LeftToRight 275 | 276 | 277 | true 278 | 279 | 280 | true 281 | 282 | 283 | true 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | Hook! 297 | 298 | 299 | 300 | :/icons/apply.ico:/icons/apply.ico 301 | 302 | 303 | 304 | 305 | 306 | 307 | Save 308 | 309 | 310 | 311 | :/icons/save_black.ico:/icons/save_black.ico 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 0 325 | 0 326 | 731 327 | 21 328 | 329 | 330 | 331 | 332 | Info 333 | 334 | 335 | 336 | 337 | 338 | Settings 339 | 340 | 341 | 342 | 343 | 344 | 345 | File 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | About 356 | 357 | 358 | 359 | 360 | true 361 | 362 | 363 | true 364 | 365 | 366 | Add new section 367 | 368 | 369 | 370 | 371 | true 372 | 373 | 374 | true 375 | 376 | 377 | Reuse imports 378 | 379 | 380 | 381 | 382 | Open PE 383 | 384 | 385 | 386 | 387 | Save PE as.. 388 | 389 | 390 | 391 | 392 | - 393 | 394 | 395 | 396 | 397 | Open hooking schema 398 | 399 | 400 | 401 | 402 | Save hooking schema 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 10 414 | 415 | 416 | 10 417 | 418 | 419 | true 420 | 421 | 422 | true 423 | 424 | 425 | true 426 | 427 | 428 | 429 | -------------------------------------------------------------------------------- /patcher/replacements.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Replacements 4 | 5 | 6 | Qt::WindowModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 410 13 | 337 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 410 25 | 320 26 | 27 | 28 | 29 | 30 | 410 31 | 500 32 | 33 | 34 | 35 | Qt::CustomContextMenu 36 | 37 | 38 | true 39 | 40 | 41 | Define replacement 42 | 43 | 44 | true 45 | 46 | 47 | false 48 | 49 | 50 | false 51 | 52 | 53 | 54 | 55 | 10 56 | 300 57 | 381 58 | 32 59 | 60 | 61 | 62 | Qt::Horizontal 63 | 64 | 65 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 66 | 67 | 68 | 69 | 70 | 71 | 10 72 | 10 73 | 391 74 | 101 75 | 76 | 77 | 78 | 79 | 0 80 | 0 81 | 82 | 83 | 84 | Replace: 85 | 86 | 87 | false 88 | 89 | 90 | false 91 | 92 | 93 | 94 | 9 95 | 96 | 97 | 5 98 | 99 | 100 | 9 101 | 102 | 103 | 5 104 | 105 | 106 | 10 107 | 108 | 109 | 5 110 | 111 | 112 | 113 | 114 | true 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | Thunk: 125 | 126 | 127 | 128 | 129 | 130 | 131 | Function: 132 | 133 | 134 | 135 | 136 | 137 | 138 | true 139 | 140 | 141 | true 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 10 154 | 110 155 | 391 156 | 191 157 | 158 | 159 | 160 | 161 | 0 162 | 0 163 | 164 | 165 | 166 | With: 167 | 168 | 169 | false 170 | 171 | 172 | false 173 | 174 | 175 | 176 | 5 177 | 178 | 179 | 20 180 | 181 | 182 | 5 183 | 184 | 185 | 5 186 | 187 | 188 | 10 189 | 190 | 191 | 5 192 | 193 | 194 | 195 | 196 | 197 | 0 198 | 0 199 | 200 | 201 | 202 | Qt::DefaultContextMenu 203 | 204 | 205 | 0 206 | 207 | 208 | 209 | Edit 210 | 211 | 212 | 213 | 214 | 0 215 | 0 216 | 361 217 | 91 218 | 219 | 220 | 221 | 222 | QLayout::SetDefaultConstraint 223 | 224 | 225 | 10 226 | 227 | 228 | 10 229 | 230 | 231 | 10 232 | 233 | 234 | 0 235 | 236 | 237 | 238 | 239 | example.dll 240 | 241 | 242 | 243 | 244 | 245 | 246 | Function: 247 | 248 | 249 | 250 | 251 | 252 | 253 | functionName 254 | 255 | 256 | 257 | 258 | 259 | 260 | Library: 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | Choose 270 | 271 | 272 | 273 | 274 | 0 275 | 0 276 | 361 277 | 91 278 | 279 | 280 | 281 | 282 | 10 283 | 284 | 285 | 10 286 | 287 | 288 | 10 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 30 298 | 16777215 299 | 300 | 301 | 302 | <html><head/><body><p>Load replacement library</p></body></html> 303 | 304 | 305 | true 306 | 307 | 308 | + 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 50 320 | 0 321 | 322 | 323 | 324 | Function: 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 50 333 | 16777215 334 | 335 | 336 | 337 | Library: 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | okCancel_buttons 351 | thunkLabel 352 | libToReplaceLabel 353 | 354 | 355 | 356 | 357 | -------------------------------------------------------------------------------- /patcher/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasherezade/IAT_patcher/e059f3388fa44797b037e1fbdc7e6e0aef4f5433/patcher/resource.h -------------------------------------------------------------------------------- /patcher/stub/Stub.cpp: -------------------------------------------------------------------------------- 1 | #include "Stub.h" 2 | 3 | bool StubParam::insertIntoBuffer(AbstractByteBuffer* buf) 4 | { 5 | if (buf == NULL) return false; 6 | 7 | offset_t fullOffset = this->m_bigOffset + this->m_smallOffset; 8 | bool isOk = buf->setNumValue(fullOffset, this->m_valueSize, this->m_value); 9 | //TODO - verify if setting succeseded? 10 | return true; 11 | } 12 | 13 | bool StubParam::readFromBuffer(AbstractByteBuffer* buf) 14 | { 15 | if (buf == NULL) return false; 16 | 17 | offset_t fullOffset = this->m_bigOffset + this->m_smallOffset; 18 | bool isOk = false; 19 | 20 | offset_t value = buf->getNumValue(fullOffset, this->m_valueSize, &isOk); 21 | if (!isOk) return false; 22 | 23 | this->m_value = value; 24 | return true; 25 | } 26 | //----- 27 | 28 | ByteBuffer* Stub::createStubBuffer() 29 | { 30 | ByteBuffer* buf = bufferStubData(); 31 | if (buf == NULL) return NULL; 32 | 33 | if (this->fillParams(buf) == false) { 34 | printf("Unable to fill params!\n"); 35 | delete buf; 36 | return NULL; 37 | } 38 | return buf; 39 | } 40 | 41 | ByteBuffer* Stub::bufferStubData() 42 | { 43 | const unsigned char* originalData = this->getData(); 44 | const bufsize_t stubSize = this->getSize(); 45 | 46 | unsigned char *stubData = new unsigned char[stubSize]; 47 | memcpy(stubData, originalData, stubSize); 48 | ByteBuffer *stubBuf = new ByteBuffer(stubData, stubSize, 0); 49 | delete []stubData; 50 | return stubBuf; 51 | } 52 | 53 | bool Stub::fillParam(size_t id, AbstractByteBuffer* buf) 54 | { 55 | StubParam *param = this->getParam(id); 56 | if (param == NULL) { 57 | //Param not found 58 | return false; 59 | } 60 | if (param->m_relativeToId != param->m_id) { 61 | offset_t dif = this->paramDistance(param->m_relativeToId, id); 62 | param->setValue(dif); 63 | } 64 | return param->insertIntoBuffer(buf); 65 | } 66 | 67 | 68 | bool Stub::fillParams(AbstractByteBuffer* buf) 69 | { 70 | std::map::iterator itr; 71 | for (itr = m_params.begin(); itr != m_params.end(); itr++) { 72 | size_t paramId = itr->first; 73 | if (fillParam(paramId, buf) == false) { 74 | return false; 75 | } 76 | } 77 | return true; 78 | } 79 | 80 | offset_t Stub::getAbsoluteValue(StubParam *param) 81 | { 82 | if (param == NULL) { 83 | return INVALID_ADDR; 84 | } 85 | 86 | offset_t myValue = param->m_value; 87 | 88 | if (param->m_relativeToId != param->m_id) { 89 | StubParam *relParam = this->getParam(param->m_relativeToId); 90 | if (relParam == NULL) return INVALID_ADDR; 91 | 92 | offset_t dif = ((int32_t) param->m_value); 93 | offset_t relValue = relParam->m_value; 94 | 95 | myValue = relParam->m_value + dif + param->getTotalOffset() + param->getValueSize(); 96 | } 97 | return myValue; 98 | } 99 | 100 | bool Stub::readParam(size_t id, AbstractByteBuffer* buf) 101 | { 102 | StubParam *param = this->getParam(id); 103 | if (param == NULL) { 104 | //Param not found 105 | return false; 106 | } 107 | if (param->readFromBuffer(buf) == false) return false; 108 | 109 | offset_t myValue = getAbsoluteValue(param); 110 | if (myValue == INVALID_ADDR) return false; 111 | 112 | param->setValue(myValue); 113 | return true; 114 | } 115 | 116 | 117 | bool Stub::readParams(AbstractByteBuffer* buf) 118 | { 119 | std::map::iterator itr; 120 | for (itr = m_params.begin(); itr != m_params.end(); itr++) { 121 | size_t paramId = itr->first; 122 | if (!readParam(paramId, buf)) { 123 | return false; 124 | } 125 | } 126 | return true; 127 | } 128 | 129 | void Stub::deleteParams() 130 | { 131 | bool isOk = false; 132 | std::map::iterator itr; 133 | for (itr = m_params.begin(); itr != m_params.end(); itr++) { 134 | StubParam* param = itr->second; 135 | delete param; 136 | } 137 | m_params.clear(); 138 | } 139 | 140 | bool Stub::containsStub(AbstractByteBuffer *buf) const 141 | { 142 | const unsigned char* stubData = this->getData(); 143 | const bufsize_t stubSize = this->getSize(); 144 | const offset_t signStart = getSignatureStart(); 145 | const offset_t signEnd = getSignatureEnd(); 146 | 147 | /* sanity check */ 148 | if (buf == NULL) return false; 149 | if (buf->getContentSize() < stubSize) return false; 150 | 151 | unsigned char* bufData = (unsigned char*) buf->getContent(); 152 | if (bufData == NULL) return false; 153 | 154 | /* sanity check */ 155 | if (stubData == NULL || signStart > signEnd || signEnd >= stubSize) { 156 | printf("Malformated stub!"); 157 | return false; 158 | } 159 | 160 | bufData += signStart; 161 | 162 | const unsigned char* cmpData = stubData + signStart; 163 | const size_t cmpSize = signEnd - signStart; 164 | 165 | int res = memcmp(cmpData, bufData, cmpSize); 166 | if (res == 0) { 167 | return true; 168 | } 169 | return false; 170 | } 171 | -------------------------------------------------------------------------------- /patcher/stub/Stub.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "../Executables.h" 5 | #include "../FuncReplacements.h" 6 | 7 | 8 | class StubParam 9 | { 10 | public: 11 | StubParam(size_t id, offset_t bigOffset, offset_t smallOffset, size_t valueSize, size_t relId) 12 | : m_id(id), m_bigOffset(bigOffset), m_smallOffset(smallOffset), m_valueSize(valueSize), m_value(0), m_relativeToId(relId) {} 13 | 14 | StubParam(size_t id, offset_t bigOffset, offset_t smallOffset, size_t valueSize) 15 | : m_id(id), m_bigOffset(bigOffset), m_smallOffset(smallOffset), m_valueSize(valueSize), m_value(0), m_relativeToId(id) {} 16 | 17 | virtual ~StubParam() { /*printf("Deleting param id = %d\n", m_id);*/ } 18 | 19 | void setValue(offset_t value) { this->m_value = value; } 20 | offset_t getValue() { return m_value; } 21 | 22 | bool insertIntoBuffer(AbstractByteBuffer* buf); 23 | bool readFromBuffer(AbstractByteBuffer* buf); 24 | 25 | offset_t getTotalOffset() { return m_bigOffset + m_smallOffset; } 26 | offset_t getBigOffset() { return m_bigOffset; } 27 | size_t getValueSize() { return m_valueSize; } 28 | 29 | protected: 30 | size_t m_id; 31 | size_t m_relativeToId; 32 | offset_t m_bigOffset; // offset of instruction in code 33 | offset_t m_smallOffset; 34 | 35 | size_t m_valueSize; 36 | offset_t m_value; 37 | 38 | friend class Stub; 39 | }; 40 | 41 | class Stub { 42 | 43 | public: 44 | enum STUB_PARAMS { 45 | NEW_EP, //LABEL_newEP 46 | DATA_RVA, //LABEL_dataRVA 47 | OEP, //LABEL_oldEP 48 | FUNC_GET_MODULE_RVA, 49 | FUNC_LOAD_LIB_RVA 50 | }; 51 | 52 | Stub() { } 53 | virtual ~Stub() { deleteParams(); } 54 | 55 | virtual bufsize_t getSize() const = 0; 56 | virtual const unsigned char* getData() const = 0; 57 | 58 | virtual offset_t getSignatureStart() const = 0; 59 | virtual offset_t getSignatureEnd() const = 0; 60 | virtual offset_t getDatastoreOffset() const = 0; 61 | 62 | ByteBuffer* createStubBuffer(); 63 | bool containsStub(AbstractByteBuffer *buf) const; 64 | 65 | size_t getParamsCount() 66 | { 67 | return m_params.size(); 68 | } 69 | 70 | offset_t getParamValue(size_t id) 71 | { 72 | if (this->m_params.find(id) == m_params.end()) { 73 | //No param with given ID 74 | return INVALID_ADDR; 75 | } 76 | return this->m_params[id]->getValue(); 77 | } 78 | 79 | bool setParam(size_t id, offset_t value) { 80 | if (this->m_params.find(id) == m_params.end()) { 81 | //No param with given ID 82 | return false; 83 | } 84 | this->m_params[id]->setValue(value); 85 | return true; 86 | } 87 | 88 | virtual bool readParams(AbstractByteBuffer* buf); 89 | 90 | protected: 91 | 92 | StubParam* getParam(size_t id) const { 93 | std::map::const_iterator itr = this->m_params.find(id); 94 | if (itr == m_params.end()) return NULL; 95 | return itr->second; 96 | } 97 | 98 | offset_t paramDistance(size_t idFrom, size_t idTo) { 99 | StubParam *param = this->getParam(idTo); 100 | StubParam *newEpParam = this->getParam(idFrom); 101 | 102 | if (param == NULL || newEpParam == NULL) return INVALID_ADDR; 103 | 104 | offset_t newEntry = newEpParam->getValue() + newEpParam->getValueSize(); 105 | offset_t oldEntry = param->getValue(); 106 | 107 | bufsize_t oldEntryOffset = param->getTotalOffset(); //toOffset 108 | 109 | offset_t dif = ((newEntry + oldEntryOffset) - oldEntry) * (-1); 110 | return dif; 111 | } 112 | 113 | offset_t getAbsoluteValue(StubParam *param); // autocalculate relative 114 | 115 | virtual bool fillParam(size_t id, AbstractByteBuffer* buf); 116 | virtual bool fillParams(AbstractByteBuffer* buf); 117 | 118 | virtual bool readParam(size_t id, AbstractByteBuffer* buf); 119 | 120 | ByteBuffer* bufferStubData(); 121 | 122 | virtual void createParams() = 0; 123 | virtual void deleteParams(); 124 | 125 | std::map m_params; 126 | }; 127 | -------------------------------------------------------------------------------- /patcher/stub/Stub32.cpp: -------------------------------------------------------------------------------- 1 | #include "Stub32.h" 2 | #include "Stub32Data.h" 3 | 4 | bufsize_t Stub32::getSize() const 5 | { 6 | return Stub32Data::size; 7 | } 8 | 9 | const unsigned char* Stub32::getData() const 10 | { 11 | return Stub32Data::data; 12 | } 13 | 14 | offset_t Stub32::getSignatureStart() const 15 | { 16 | return Stub32Data::pointers[2] + 1 + sizeof(DWORD); 17 | } 18 | 19 | offset_t Stub32::getSignatureEnd() const 20 | { 21 | return Stub32Data::pointers[3]; 22 | } 23 | 24 | offset_t Stub32::getDatastoreOffset() const 25 | { 26 | return Stub32Data::pointers[DATA_RVA]; 27 | } 28 | 29 | void Stub32::createParams() 30 | { 31 | this->m_params[NEW_EP] = new StubParam(NEW_EP, Stub32Data::pointers[0], 1, sizeof(DWORD)); 32 | this->m_params[DATA_RVA] = new StubParam(DATA_RVA, Stub32Data::pointers[1], 1, sizeof(DWORD)); 33 | this->m_params[OEP] = new StubParam(OEP, Stub32Data::pointers[2], 1, sizeof(DWORD) , NEW_EP); 34 | 35 | this->m_params[FUNC_LOAD_LIB_RVA] = new StubParam(FUNC_LOAD_LIB_RVA, Stub32Data::pointers[3], 1, sizeof(DWORD)); 36 | this->m_params[FUNC_GET_MODULE_RVA] = new StubParam(FUNC_GET_MODULE_RVA, Stub32Data::pointers[4], 1, sizeof(DWORD)); 37 | } 38 | -------------------------------------------------------------------------------- /patcher/stub/Stub32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Stub.h" 3 | 4 | class Stub32 : public Stub { 5 | public: 6 | Stub32() : Stub() { createParams(); } 7 | 8 | virtual bufsize_t getSize() const; 9 | virtual const unsigned char* getData() const; 10 | 11 | virtual offset_t getSignatureStart() const; 12 | virtual offset_t getSignatureEnd() const; 13 | virtual offset_t getDatastoreOffset() const; 14 | 15 | protected: 16 | virtual void createParams(); 17 | }; 18 | -------------------------------------------------------------------------------- /patcher/stub/Stub32Data.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Stub32Data { 4 | 5 | const unsigned char data[] = { 6 | 0xb8, 0x11, 0x11, 0x11, 7 | 0x11, 0xbb, 0x22, 0x22, 8 | 0x22, 0x22, 0xe8, 0x05, 9 | 0x00, 0x00, 0x00, 0xe9, 10 | 0x64, 0x56, 0x34, 0x12, 11 | 0x83, 0xc0, 0x0f, 0x8b, 12 | 0x3c, 0x24, 0x29, 0xc7, 13 | 0x01, 0xfb, 0xe8, 0x4a, 14 | 0x00, 0x00, 0x00, 0x85, 15 | 0xc0, 0x75, 0x01, 0xc3, 16 | 0x89, 0xc1, 0x51, 0xbe, 17 | 0x22, 0x22, 0x22, 0x22, 18 | 0x01, 0xfe, 0xff, 0x16, 19 | 0x85, 0xc0, 0x75, 0x0b, 20 | 0xe8, 0x3c, 0x00, 0x00, 21 | 0x00, 0x85, 0xc0, 0x75, 22 | 0xf7, 0xeb, 0xdb, 0x89, 23 | 0xc1, 0xe8, 0x23, 0x00, 24 | 0x00, 0x00, 0x85, 0xc0, 25 | 0x74, 0xd0, 0x50, 0x51, 26 | 0xbe, 0x22, 0x22, 0x22, 27 | 0x22, 0x01, 0xfe, 0xff, 28 | 0x16, 0x89, 0xc6, 0xe8, 29 | 0x0d, 0x00, 0x00, 0x00, 30 | 0x85, 0xc0, 0x75, 0x01, 31 | 0xc3, 0x85, 0xf6, 0x74, 32 | 0x02, 0x89, 0x30, 0xeb, 33 | 0xd6, 0xe8, 0x07, 0x00, 34 | 0x00, 0x00, 0x85, 0xc0, 35 | 0x74, 0x02, 0x01, 0xf8, 36 | 0xc3, 0x8b, 0x03, 0x83, 37 | 0xc3, 0x04, 0xc3 38 | }; 39 | 40 | DWORD pointers[] = { 41 | 0x00, //LABEL_newEP 42 | 0x05, //LABEL_dataRVA 43 | 0x0f, //LABEL_oldEP 44 | 0x2b, //LABEL_loadLibrary 45 | 0x50 //LABEL_GetProcAddress 46 | }; 47 | 48 | const size_t size = sizeof(data); 49 | 50 | };//Stub32Data 51 | -------------------------------------------------------------------------------- /patcher/stub/Stub64.cpp: -------------------------------------------------------------------------------- 1 | #include "Stub64.h" 2 | #include "Stub64Data.h" 3 | 4 | bufsize_t Stub64::getSize() const 5 | { 6 | return Stub64Data::size; 7 | } 8 | 9 | const unsigned char* Stub64::getData() const 10 | { 11 | return Stub64Data::data; 12 | } 13 | 14 | offset_t Stub64::getSignatureStart() const 15 | { 16 | return Stub64Data::pointers[2] + 1 + sizeof(DWORD); 17 | } 18 | 19 | offset_t Stub64::getSignatureEnd() const 20 | { 21 | return Stub64Data::pointers[3]; 22 | } 23 | 24 | offset_t Stub64::getDatastoreOffset() const 25 | { 26 | return Stub64Data::pointers[DATA_RVA]; 27 | } 28 | 29 | 30 | void Stub64::createParams() 31 | { 32 | this->m_params[NEW_EP] = new StubParam(NEW_EP, Stub64Data::pointers[0], 3, sizeof(DWORD)); 33 | this->m_params[DATA_RVA] = new StubParam(DATA_RVA, Stub64Data::pointers[1], 3, sizeof(DWORD)); 34 | this->m_params[OEP] = new StubParam(OEP, Stub64Data::pointers[2], 1, sizeof(DWORD) , NEW_EP); 35 | 36 | this->m_params[FUNC_LOAD_LIB_RVA] = new StubParam(FUNC_LOAD_LIB_RVA, Stub64Data::pointers[3], 2, sizeof(DWORD), NEW_EP); 37 | this->m_params[FUNC_GET_MODULE_RVA] = new StubParam(FUNC_GET_MODULE_RVA, Stub64Data::pointers[4], 2, sizeof(DWORD), NEW_EP); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /patcher/stub/Stub64.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Stub.h" 3 | 4 | class Stub64 : public Stub { 5 | public: 6 | Stub64() : Stub() { createParams(); } 7 | virtual bufsize_t getSize() const; 8 | virtual const unsigned char* getData() const; 9 | 10 | virtual offset_t getSignatureStart() const; 11 | virtual offset_t getSignatureEnd() const; 12 | virtual offset_t getDatastoreOffset() const; 13 | 14 | protected: 15 | virtual void createParams(); 16 | }; 17 | -------------------------------------------------------------------------------- /patcher/stub/Stub64Data.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Stub64Data { 4 | 5 | const unsigned char data[] = { 6 | 0x48, 0xc7, 0xc0, 0x11, 7 | 0x11, 0x11, 0x11, 0x48, 8 | 0xc7, 0xc3, 0x22, 0x22, 9 | 0x22, 0x22, 0xe8, 0x05, 10 | 0x00, 0x00, 0x00, 0xe9, 11 | 0x60, 0x56, 0x34, 0x12, 12 | 0x48, 0x83, 0xc0, 0x13, 13 | 0x4c, 0x8b, 0x3c, 0x24, 14 | 0x49, 0x29, 0xc7, 0x4c, 15 | 0x01, 0xfb, 0xe8, 0x53, 16 | 0x00, 0x00, 0x00, 0x85, 17 | 0xc0, 0x75, 0x01, 0xc3, 18 | 0x48, 0x89, 0xc1, 0x49, 19 | 0x89, 0xde, 0x48, 0x8b, 20 | 0x1c, 0x24, 0xff, 0x15, 21 | 0xed, 0xa1, 0x05, 0x00, 22 | 0x48, 0x85, 0xc0, 0x4c, 23 | 0x89, 0xf3, 0x75, 0x0b, 24 | 0xe8, 0x3e, 0x00, 0x00, 25 | 0x00, 0x85, 0xc0, 0x75, 26 | 0xf7, 0xeb, 0xd3, 0x48, 27 | 0x89, 0xc1, 0xe8, 0x23, 28 | 0x00, 0x00, 0x00, 0x85, 29 | 0xc0, 0x74, 0xc7, 0x48, 30 | 0x89, 0xc2, 0xe8, 0x2e, 31 | 0x00, 0x00, 0x00, 0x48, 32 | 0x89, 0xc6, 0xe8, 0x0f, 33 | 0x00, 0x00, 0x00, 0x85, 34 | 0xc0, 0x75, 0x01, 0xc3, 35 | 0x48, 0x85, 0xf6, 0x74, 36 | 0x03, 0x48, 0x89, 0x30, 37 | 0xeb, 0xd5, 0xe8, 0x08, 38 | 0x00, 0x00, 0x00, 0x85, 39 | 0xc0, 0x74, 0x03, 0x4c, 40 | 0x01, 0xf8, 0xc3, 0x48, 41 | 0x31, 0xc0, 0x8b, 0x03, 42 | 0x48, 0x83, 0xc3, 0x04, 43 | 0xc3, 0x53, 0x48, 0x83, 44 | 0xec, 0x20, 0xff, 0x15, 45 | 0xb4, 0x0f, 0x00, 0x00, 46 | 0x48, 0x83, 0xc4, 0x20, 47 | 0x5b, 0xc3 48 | }; 49 | 50 | DWORD pointers[] = { 51 | 0x00, 52 | 0x07, 53 | 0x13, 54 | 0x3a, 55 | 0x9a 56 | }; 57 | 58 | const size_t size = sizeof(data); 59 | 60 | };//Stub64Data 61 | -------------------------------------------------------------------------------- /stub/hexf.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include 3 | 4 | int main(int argc, char* argv[]) 5 | { 6 | if (argc < 2) { 7 | printf("No fileName supplied!\n"); 8 | return -1; 9 | } 10 | FILE *fp = fopen(argv[1], "rb"); 11 | if (fp == NULL) { 12 | printf("Unable to open the file!\n"); 13 | return -1; 14 | } 15 | int brk = 4; 16 | int i = 0; 17 | 18 | char c = 0; 19 | while(!feof(fp)) { 20 | c = fgetc (fp); 21 | 22 | if (i == brk) { 23 | i = 0; 24 | printf("\n"); 25 | } 26 | i++; 27 | printf("0x%2.2x, ", c & 0x0FF); 28 | } 29 | printf("0x90\n"); 30 | return 0; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /stub/stub32.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | start: 3 | LABEL_newEP: 4 | mov EAX, 11111111h ; new EP 5 | LABEL_dataRVA: 6 | mov EBX, 22222222h ; data RVA 7 | call init 8 | after_init: 9 | LABEL_oldEP: 10 | jmp 12345678h ; oep 11 | init: 12 | add EAX, (after_init - start) 13 | mov EDI, [ESP] 14 | sub EDI, EAX ; ImageBase 15 | add EBX, EDI 16 | load_lib: 17 | call get_saved_rva 18 | TEST EAX, EAX 19 | JNZ SHORT load_next_lib 20 | RET 21 | 22 | load_next_lib: 23 | mov ECX, EAX 24 | push ECX 25 | LABEL_LoadLibrary: 26 | MOV ESI, 22222222h 27 | ADD ESI, EDI 28 | CALL [ESI] ; call LoadLibraryA 29 | test EAX, EAX 30 | jnz load_function 31 | 32 | skip_functions: ; if DLL not found, skip mapped 33 | call get_saved_value 34 | test eax,eax 35 | jne skip_functions 36 | jmp load_lib 37 | 38 | load_function: 39 | mov ECX, EAX 40 | call get_saved_rva 41 | TEST EAX, EAX 42 | JZ SHORT load_lib 43 | 44 | push EAX 45 | push ECX 46 | LABEL_GetProcAddress: 47 | MOV ESI, 22222222h 48 | ADD ESI, EDI 49 | CALL DWORD NEAR [ESI] ; call GetProcAddress 50 | mov ESI, EAX ; ESI <- Handle 51 | call get_saved_rva ; thunk to fill or to skip 52 | TEST EAX, EAX ; is thunk empty? 53 | jne overwrite_thunk 54 | ret ; malformed data, just finish... 55 | 56 | overwrite_thunk: 57 | TEST ESI, ESI ; is Handle Empty? 58 | je _end_load_function 59 | MOV [EAX], ESI 60 | _end_load_function: 61 | JMP SHORT load_function 62 | 63 | get_saved_rva: 64 | call get_saved_value 65 | test eax,eax 66 | jz _end_get_saved_rva 67 | ADD EAX, EDI ; ImgBase 68 | _end_get_saved_rva: 69 | ret 70 | 71 | get_saved_value: 72 | mov eax, dword [EBX] 73 | ADD EBX, 0X4 74 | ret 75 | ;-------- 76 | ;Data: 77 | dd (LABEL_newEP - start) 78 | dd (LABEL_dataRVA - start) 79 | dd (LABEL_oldEP - start) 80 | dd (LABEL_LoadLibrary - start) 81 | dd (LABEL_GetProcAddress - start) 82 | -------------------------------------------------------------------------------- /stub/stub64.asm: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | start: 3 | LABEL_newEP: 4 | mov rax, 11111111h ; new EP 5 | LABEL_dataRVA: 6 | mov rbx, 22222222h ; data RVA 7 | call init 8 | after_init: 9 | LABEL_oldEP: 10 | jmp 12345678h ; oep 11 | init: 12 | add rax, (after_init - start) 13 | mov r15, [rsp] 14 | sub r15, rax ;r15 -> ImageBase 15 | add rbx, r15 16 | load_lib: 17 | call get_saved_rva 18 | TEST EAX, EAX 19 | JNZ SHORT load_next_lib 20 | RET 21 | 22 | load_next_lib: 23 | mov rcx, rax 24 | mov r14,rbx 25 | mov rbx,[rsp] 26 | LABEL_LoadLibrary: 27 | CALL QWORD NEAR [RIP+0X5A1ED] ; call LoadLibraryA 28 | test rax, rax 29 | mov rbx,r14 30 | jnz load_function 31 | skip_functions: ; if DLL not found, skip mapped 32 | call get_saved_value 33 | test eax,eax 34 | jne skip_functions 35 | jmp load_lib 36 | 37 | load_function: 38 | mov rcx, rax 39 | call get_saved_rva 40 | TEST EAX, EAX 41 | JZ SHORT load_lib 42 | 43 | mov rdx, rax 44 | CALL getProc 45 | mov rsi, rax ; RSI <- Handle 46 | call get_saved_rva ; thunk to fill or to skip 47 | TEST EAX, EAX ; is thunk empty? 48 | jne overwrite_thunk 49 | ret ; malformed data, just finish... 50 | 51 | overwrite_thunk: 52 | TEST rsi, rsi ; is Handle Empty? 53 | je _end_load_function 54 | MOV [rax], rsi 55 | _end_load_function: 56 | JMP SHORT load_function 57 | 58 | get_saved_rva: 59 | call get_saved_value 60 | test eax,eax 61 | jz _end_get_saved_rva 62 | ADD rax, r15 ; ImgBase 63 | _end_get_saved_rva: 64 | ret 65 | 66 | get_saved_value: 67 | xor rax, rax 68 | mov eax, dword [rbx] 69 | ADD rbx, 0X4 70 | ret 71 | ;>------ 72 | getProc: 73 | PUSH RBX 74 | SUB RSP, 0X20 75 | LABEL_GetProcAddress: 76 | CALL QWORD NEAR [RIP+0XFB4] ;[KERNEL32.dll].GetProcAddress 77 | ADD RSP, 0X20 78 | POP RBX 79 | RET 80 | ;<------- 81 | ;-------- 82 | ;Data: 83 | dd (LABEL_newEP - start) 84 | dd (LABEL_dataRVA - start) 85 | dd (LABEL_oldEP - start) 86 | dd (LABEL_LoadLibrary - start) 87 | dd (LABEL_GetProcAddress - start) --------------------------------------------------------------------------------