Как проверить, созданы ли новые потоки внутри сторонней DLL в приложении Visual C ++ - PullRequest
0 голосов
/ 08 мая 2019

В настоящее время я борюсь с проблемой проверки того, создаются ли новые потоки внутри файла dll, которые мы вызываем из нашего кода в приложении Visual C ++. Предпочтительно я хотел видеть потребление ресурсов и стеки вызовов, если есть какие-то потоки, созданные внутри dll. Мне нужно, чтобы это происходило в запущенном приложении, а не в режиме отладки. Кто-нибудь из вас знает, как мы можем решить эту проблему?

1 Ответ

2 голосов
/ 09 мая 2019

Заставьте приложение перехватить API CreateThread в своем коде запуска, предпочтительно до инициализации DLL. Это глубокая магия, хотя; ты должен точно знать, что ты делаешь.

РЕДАКТИРОВАТЬ: если вам нужно спросить, вы, вероятно, не до этой задачи. Действуйте на свой страх и риск.

Общая идея заключается в следующем: используйте дизассемблер, чтобы увидеть первые несколько команд CreateThread() в работающем процессе. В основном исполняемом коде исправьте память CreateThread, чтобы вставить команду JMP в свою собственную функцию батута. Напишите в сборке батут, который будет вызывать вашу собственную функцию ловушки, возможно, повторите некоторые команды из исправленной части и вернитесь к непатченной части CreateThread ().

Это не предназначено для производства - только для запуска исполняемого файла на вашем компьютере с известной версией CreateThread, поскольку это будет зависеть от содержимого CreateThread. В некотором смысле, приложение становится собственным отладчиком, но это не тот отладчик, который DLL может легко заметить.

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

Естественно, вы должны иметь в виду битность.

В качестве альтернативы вы можете исправить начало CreateThread, чтобы вызвать исключение (например, INT 3), и использовать обработчик векторных исключений, чтобы его перехватить. Функции Win32 API имеют 2-байтовый NOP в начале, достаточный для исправления.

РЕДАКТИРОВАТЬ: попробовал VEH подход, кажется, чище. Теперь, на моей машине, первая команда CreateThread - MOV EDI, EDI - это фактически 2-байтовый запрет, идеально подходящий для исправления. Итак, обработчик исключений выглядит так:

LONG NTAPI OnExc(_EXCEPTION_POINTERS* Exc)
{
    if (Exc->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
    { //Feel free to add an extra check for exception address
      //Just in case there are rogue INT 3's elsewhere

        wprintf(L"Yay, thread created\n");
        //Not a good idea to do I/O from the exception handler :)

        //Continue from the next command after INT 3
        Exc->ContextRecord->Eip++; 
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    else
        return EXCEPTION_CONTINUE_SEARCH;
}

И код перехвата идет:

AddVectoredExceptionHandler(1, OnExc);

HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
unsigned char* pCreateThread = (unsigned char*)GetProcAddress(hKernel32, "CreateThread");

//Allow writing to the memory block where CreateThread is
MEMORY_BASIC_INFORMATION mi;
VirtualQuery(pCreateThread, &mi, sizeof mi);
DWORD dw;
VirtualProtect(mi.BaseAddress, mi.RegionSize, PAGE_EXECUTE_READWRITE, &dw);

//Check if the first two bytes are indeed MOV EDI, EDI
if (pCreateThread[0] == 0x8b && pCreateThread[1] == 0xff)
{
    //And patch!
    pCreateThread[0] = 0xcc; //Replace with INT 3 
    pCreateThread[1] = 0x90; //Replace with NOP
}

Вот и все, подсел. Не стесняйтесь проверить его, позвонив по номеру _beginthread или _beginthreadex, но не в отладчике. Отладчик будет перехватывать и обрабатывать INT 3 до того, как это сделает обработчик исключений.

Это все 32-битный код. Даже не посмотрел на то, на что будет похож Win64.

Существуют и другие аспекты работы, которые я не охватывал. В частности, если рассматриваемая DLL загружается статически, и она создает поток (ы) в своем коде запуска, и , если хук установлен в [Win] main (), тогда крюк не поймает эти нити. Поскольку это ваш проект, у меня нет возможности это проверить.

Еще один способ улучшения - если вы хотите поймать результат создания потока (например, идентификатор потока), эта техника сама по себе не подойдет. Вам нужно подключить не только точку входа CreateThread, но и выход.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...