Почему эта обходная функция приводит к сбою программы? - PullRequest
1 голос
/ 29 мая 2019

У меня есть прокси-сервер dxgi.dll, и я пытаюсь отключить функцию Present в оригинальном dxgi.dll для отображения объектов на экране..Dll успешно загружен и объезд размещен.Однако объезд сбивает программу, как только мой новый подарок называется. Имейте в виду, что .dll и программы являются 64-битными.

Ниже приведено изображение того, как функция выглядит в памяти до модификации (начало выделено):

Хорошо, я только что узнал, что я не могу публиковать изображения прямо здесь, если у меня нет 10 репутации, поэтому используйте эту ссылку (замените DOT): https://imgur DOTcom / a / Jf53dYc

Я не уверен, где именно происходит сбой, я полагаю, что программа продолжает работать некоторое время, но она определенно падает в середине / вскоре после вызова обходного Present, я знаю этопотому что я могу записать указатель на параметр SwapChain в файл из обхода Present до его сбоя.

Я нашел оригинальный адрес функции Present с помощью IDA.Вы можете увидеть, что говорит IDA о функции на картинке в галерее imgur.

Я смотрю на память и пытаюсь выяснить, что не так, когда я следую за прыжками, используя Cheat engine, который онипривести к правильным местам, тем не менее что-то в обход делает сбой программы.Переопределенные коды операций также, кажется, заменены должным образом.

Я пытался изменить соглашение о вызовах и тип возвращаемого значения в моей функции Present, я прочитал в руководстве по перехвату dxgi, что возвращаемый тип был HRESULT, я пыталсяизменить на это безрезультатно.Что касается соглашения о вызовах, я попробовал WINAPI.

Я также немного изучил, не поврежден ли стек или регистры моим обходом функции.Однако я не очень хорошо разбираюсь в ассемблере и не могу с уверенностью сказать, так ли это.

У меня есть класс с именем Core, который занимается перехватом, вот файл заголовка с некоторыми соответствующими определениями:

#pragma once
#include <iostream>
#include <Windows.h>
#include <intrin.h>
#include <dxgi.h>
#include <fstream>

// Seems my C++ doesn't have QWORD predefined, defining it myself
typedef unsigned __int64 QWORD; 

// Definition of the structure of the DXGI present function
typedef __int64 (__fastcall* PresentFunction)(IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags);

class Core
{
private:
    QWORD originalDllBaseAddress;
    QWORD originalPresentFunctionOffset;

public:
    void Init();
    bool Hook(PresentFunction originalFunction, void* newFunction, int bytes);
    ~Core();
};

Init запускает процесс, получая соответствующие адреса:

void Core::Init()
{
originalDllBaseAddress = (QWORD)GetModuleHandleA("dxgi_.dll");
originalPresentFunctionOffset = 0x5070;
originalPresentFunction = (PresentFunction)(originalDllBaseAddress + (QWORD)originalPresentFunctionOffset);
Hook(originalPresentFunction, FixAndReturn, 14);
}

Hook пытается выполнить переход по целевому адресу, Я твердо верю, что проблема где-то здесь, (комментарии теперь изменили мое мнение, возможно, это как-то связано со сборкой, регистрами или стеком), более конкретно, с назначениями originalFunction:

bool Core::Hook(PresentFunction originalFunction, void* newFunction, int length)
{
    DWORD oldProtection;

    VirtualProtect(originalFunction, length, PAGE_EXECUTE_READWRITE, &oldProtection);

    memset(originalFunction, 0x90, length);

    // Bytes are flipped (because of endianness), could alternatively use _byteswap_uint64()
    *(QWORD*)originalFunction = 0x0000000025FF;

    // The kind of jump I'm doing here seems to only use 6 bytes,
    // and then grabs the subsequent memory address,
    // I'm not quite sure if I'm doing this right
    *(QWORD*)((QWORD)originalFunction + 6) = (QWORD)newFunction;

    DWORD temp;
    VirtualProtect(originalFunction, length, oldProtection, &temp);

    originalPresentFunction = (PresentFunction)((QWORD)originalFunction + length);

    presentAddr = (QWORD)Present;
    jmpBackAddr = (QWORD)originalPresentFunction;

    return true;
}

Я пробовал много вещей, когда дело доходит до записи байтов в память, но ни одна из них не устранила мою проблему.

Присвоение функции «originalPresentFunction» в конце функции - это адрес, который объездпопытается вернуться к.

Вот определение функции обхода, расположенной в Core.cpp:

__int64 __fastcall Present(IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags)
{
    //The program crashes with and without these file writes.
    std::ofstream file;
    file.open("HELLO FROM PRESENT.txt");
    file << pSwapChain;
    file.close();

    return originalPresentFunction(pSwapChain, SyncInterval, Flags);
}

Это функция при вызовеЭд, что вызывает сбой.Как видите, я записываю здесь параметр pSwapChain в файл.Я сделал это, чтобы проверить, передаются ли параметры из исходной функции.Эта запись успешна, и содержимое файла выглядит как правильный указатель.таким образом сбой происходит после этой записи.FixAndReturn () - это функция сборки.

includelib legacy_stdio_definitions.lib

.data
extern presentAddr : qword
extern jmpBackAddr : qword

; This performs instructions originally performed by dxgi.dll in the
; memory that we've replaced, and then returns

.code
    FixAndReturn PROC 
        call [presentAddr]
        mov [rsp+10h],rbx
        mov [rsp+20h],rsi
        push rbp
        push rdi
        push r14
        jmp qword ptr [jmpBackAddr]
    FixAndReturn ENDP
end

Я загрузил весь код на Github, если требуется больше кода: https://github.com/techiew/KenshiDXHook

1 Ответ

0 голосов
/ 09 июня 2019

Прошло много времени, я был занят другими делами, но теперь я успешно сделал функцию обхода.

После просмотра ресурсов в Интернете и много размышлений.Ответ довольно прост.В моем FixAndReturn коде сборки все, что мне нужно сделать, это jmp для функции обхода, вызов не требуется. вызов может излишне изменить то, что нам не нужно, и наша функция обхода уже идентична исходной функции с точки зрения параметров и тому подобного, поэтому она уже будет считывать параметры из того же места, гдеоригинальный вызов функции разместил их.Это означает, что jmp будет отлично работать для запуска функции обхода.Для сборки не требуется никаких дополнительных толчков или треск.

Вот основной обзор процесса:

  • Наш крюк размещается путем помещения jmp в начало нашей сборкиcode.
  • Наш ассемблерный код немедленно переходит к нашей функции обхода / перехвата.
  • Когда функция обхода завершается, он возвращает вызов функции.

Этот вызов функции использует typedef, который идентичен исходной функции, которую мы перехватили.Это выглядит так:

typedef HRESULT (__fastcall* PresentFunction)(IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags);

Возврат функции с использованием typedef выполняется следующим образом с исходными значениями аргумента:

return ((PresentFunction)coreRef->newPresentReturn)(swapChain, syncInterval, flags);

В основном, здесь происходит следующее: адрес следует справапосле того, как наша вторая сборка кода jmp возвращает указание на нашу функцию обхода и вызывается как функция, таким образом, мы переходим в обход, возвращаемся назад и выполняем исходный код.(coreRef-> newPresentReturn содержит адрес сразу после инструкции jmp ).

Теперь мы придерживаемся соглашения о вызовах исходной функции Present, и параметры, которые мы передаем, помещаются вправильные места, регистры, стек и все, что не повреждено.

Используемый ресурс: Guidedhacking.com - перехват D3D11 barebones

Полный код на моемGithub: https://github.com/techiew/KenshiHook

...