我可以挂钩任何其他函数,但不能挂钩ExitProcess。

这是演示此代码的代码:

#include <iostream>
#include <cstdlib>

#include <Windows.h>
#include <Psapi.h>

void __stdcall NewSleep(DWORD milliseconds)
{
        std::cout << "Sleep." << std::endl;

        std::cin.get();
}

void __stdcall NewExitProcess(UINT exitCode)
{
        std::cout << "ExitProcess." << std::endl;

        std::cin.get();
}

FARPROC f1 = NULL;
FARPROC f2 = NULL;

int main()
{
        HMODULE kernel32Module = GetModuleHandle("KERNEL32.dll");
        f1 = GetProcAddress(kernel32Module, "Sleep");
        f2 = GetProcAddress(kernel32Module, "ExitProcess");

        std::cout << f1 << std::endl;

        unsigned char* baseAddress = (unsigned char*)GetModuleHandle(NULL);

        IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)baseAddress;
        IMAGE_NT_HEADERS* inh = (IMAGE_NT_HEADERS*)(baseAddress + idh->e_lfanew);
        IMAGE_IMPORT_DESCRIPTOR* iid = (IMAGE_IMPORT_DESCRIPTOR*)(baseAddress + inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

        for (IMAGE_IMPORT_DESCRIPTOR* i = iid; i->Name != 0; ++i)
        {
                std::string moduleName = (char*)(baseAddress + i->Name);

                if (moduleName == "KERNEL32.dll")
                {
                        IMAGE_THUNK_DATA* itd = (IMAGE_THUNK_DATA*)(baseAddress + i->FirstThunk);

                        for (IMAGE_THUNK_DATA* j = itd; j->u1.Function != 0; ++j)
                        {
                                if ((FARPROC)j->u1.Function == f1)
                                {
                                        DWORD oldProtect = 0;
                                        VirtualProtect(&j->u1.Function, sizeof(DWORD), PAGE_READWRITE, &oldProtect);
                                        j->u1.Function = (DWORD)&NewSleep;
                                        VirtualProtect(&j->u1.Function, sizeof(DWORD), oldProtect, &oldProtect);
                                }

                                if ((FARPROC)j->u1.Function == f2)
                                {
                                        DWORD oldProtect = 0;
                                        VirtualProtect(&j->u1.Function, sizeof(DWORD), PAGE_READWRITE, &oldProtect);
                                        j->u1.Function = (DWORD)&NewExitProcess;
                                        VirtualProtect(&j->u1.Function, sizeof(DWORD), oldProtect, &oldProtect);
                                }
                        }

                        break;
                }
        }

        Sleep(0);
        Sleep(0);

        ExitProcess(0);
        //Crash.

        std::cin.sync();
        std::cin.get();
        return EXIT_SUCCESS;
}

它调用了钩子(Hook)函数,但是当NewExitProcess返回时,出现访问冲突。对Sleep的调用很好,就像ExitProcess之外的任何其他钩子(Hook)函数一样。

编辑:虽然挂钩ExitThread时,我得到相同的问题。

最佳答案

查找ExitProcess的函数声明时,您会发现类似以下内容:

WINBASEAPI
DECLSPEC_NORETURN
VOID
WINAPI
ExitProcess(
    _In_ UINT uExitCode
    );

有趣的部分是DECLSPEC_NORETURN,它被定义为__declspec(noreturn)。这也是ExitThread函数使用的属性,它也导致您崩溃。 Looking up on the docs,我们发现:



根据您的发现,它不仅用于禁用编译器警告,而且还用于优化。这也解释了为什么它可以在“调试”模式下工作。

我无法为此找到一个好的解决方案,因为您正在与优化程序作战。您在注释中编写的解决方案对我不起作用(VS2013, Release模式,/ O2)。我想出了一些愚蠢的东西,但这似乎对我有用:
int *ptr = (int*)&ExitProcess;
ptr++;
ptr--;
((VOID (WINAPI*)(UINT))ptr)(0);

通常,钩住另一个未知程序的ExitProcess应该始终退出当前线程,因为它可能被编译为没有任何代码可返回。

10-08 00:32