Есть ли лучший способ изменить функцию, которая постоянно вызывается многими потоками? - PullRequest
0 голосов
/ 04 мая 2020

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

Моя первоначальная идея заключалась в том, чтобы приостановить другие потоки, но это не идеально в моей ситуации. Решил использовать atomi c operations. Это какой-то псевдокод, который я придумал, чтобы проверить свою идею:

#include <thread>
#include <cstdio>
#include <Windows.h>

void hook()
{
    puts("Function hooked");
}

void func()
{
    puts("Actual function");
}

void __declspec(noreturn) thread()
{
    while (true)
        func(); //Hammer the function
}

int main()
{
    DWORD old;
    VirtualProtect(func, 0x5, PAGE_EXECUTE_READWRITE, &old); //change memory protections so we can write

    //create the threads and release the handle (we don't own them)
    std::thread t{thread};
    t.detach();

    std::thread s{thread};
    s.detach();

    std::thread z{thread};
    z.detach();

    //sleep 1 second to allow `threads t, s, z` to run
    Sleep(1000);

    //Hook the function
    _InterlockedExchange8(reinterpret_cast<char*>(func), 0xe9);
    const auto diff = static_cast<ULONG>(reinterpret_cast<ULONG>(hook) - reinterpret_cast<ULONG>(func)) - 5;
    _InterlockedExchange(reinterpret_cast<unsigned long*>(reinterpret_cast<PUCHAR>(func) + 1), diff);

    //Halt the current thread until program is terminated
    WaitForSingleObject(GetCurrentProcess(), INFINITE);
}

Это работает, но безопасно ли это? Или это работает только из-за времени, необходимого для вызова puts и вернуться? Есть ли лучший способ сделать это?

Спасибо.

1 Ответ

0 голосов
/ 04 мая 2020

Это зависит от ряда факторов, и существует ряд возможных решений.

  1. Можно ли написать функцию так, чтобы она была реентерабельной? То есть, можете ли вы сделать функцию такой, чтобы не имело значения, используют ли ее одновременно несколько потоков? Отсутствие переменных stati c (константы stati c в порядке) и отсутствие вызовов нереентерабельных подпрограмм сделали бы его самим не реентерабельным. В этом случае синхронизация не требуется.
  2. Похоже, вы сами пишете код для функции. Почему бы просто не поместить в функцию мьютекс stati c и заблокировать его, пока вы выполняете код, который нарушает критерии повторного входа? Критический раздел может быть очень маленькой частью всей функции и кратким описанием приостановки потока.
  3. Похоже, вы пишете код для других потоков. Вы можете совместно использовать глобальный мьютекс, который блокируется каждым потоком при вызове функции.
  4. Если другие потоки находятся в отдельных процессах, вам следует изучить возможности Межпроцессной синхронизации в Windows. Это позволит вам создать системный объект мьютекса и предоставить его всем процессам для их использования, чтобы разрешить синхронизированный доступ к функции.

Обратите внимание, что решения 2, 3 и 4 приостанавливает потоки, поэтому это может быть неприемлемо в вашей ситуации.

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

...