利用Detours进行API拦截

原文地址:http://www.codeproject.com/Articles/30140/API-Hooking-with-MS-Detours

在这篇文章里,我将要介绍API拦截技术的相关理论和实现方式。API拦截是一项强大的技术,他让你可以拦截某些函数,重定位到自定义的函数上。在将控制权交给原始API之前,你可以在这个自定义的函数里做任何想做的事。

1.介绍

本文中,我将讨论API拦截主题。API拦截包括拦截程序中调用的函数,和重定向被拦截的的函数到另一个我们定义的函数。通过拦截,函数的参数可以被修改;而且你可以通过修改返回码来误导源程序,比如函数调用本应该返回正确的结果,你却返回了一个错误的,从而使源程序获的错误的返回值。当然你还可以利用拦截做许多其他的事情。所有这些都在实际函数被调用之前进行。在你修改/储存/扩展了原函数/参数后,控制权被传递回原来应调用的函数。本文要求读者对C++有深入的了解。我将使用微软的Detours库(免费下载)实现API拦截。为了能够编译提供的代码例子,你需要运行Detours库自带的Makefile文件,编译出所需的库文件和其他的东西。具体的操作步骤可以在MSDN forum里面或者其他网站上找到。由于篇幅所限,本文中的代码例子没有注释,但是有很多解释说明。附件中的代码有详细的注释。

2.开始拦截之旅:传统的API拦截技术

在介绍Detours库之前,我讨论一下传统使用的API拦截方式,通过用自定义函数的地址代替API函数的地址。这其实只是API拦截的一种方式,其他的方法包括修改导入地址表;使用代理DLL和manifest文件;在内核地址空间加载驱动等等。我将使用的技术是很基本的,被拦截的API每次都需要脱钩,这在多线程的程序里会造成并发性冲突。有一个解决的方法是在其他地方为原来的函数分配一块内存,然后设置一个钩子来阻止不停的重写detour。在本文中,为了使代码和调试变得简单,我没有使用这种方式。当然这也不是最好的方式。由于这篇文章主要是讨论利用detours进行API拦截,所以冲突问题的解决就不是特别重要了。

#include <windows.h>

#define SIZE 6

typedef int (WINAPI *pMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT);
int WINAPI MyMessageBoxW(HWND, LPCWSTR, LPCWSTR, UINT);

void BeginRedirect(LPVOID);

pMessageBoxW pOrigMBAddress = NULL;
BYTE oldBytes[SIZE] = {0};
BYTE JMP[SIZE] = {0};
DWORD oldProtect, myProtect = PAGE_EXECUTE_READWRITE;

INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
    switch(Reason)
    {
    case DLL_PROCESS_ATTACH:
        pOrigMBAddress = (pMessageBoxW)
            GetProcAddress(GetModuleHandle("user32.dll"), 
                           "MessageBoxW");
        if(pOrigMBAddress != NULL)
            BeginRedirect(MyMessageBoxW);    
        break;
    case DLL_PROCESS_DETACH:
        memcpy(pOrigMBAddress, oldBytes, SIZE);
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;
    }
    return TRUE;
}

void BeginRedirect(LPVOID newFunction)
{
    BYTE tempJMP[SIZE] = {0xE9, 0x90, 0x90, 0x90, 0x90, 0xC3};
    memcpy(JMP, tempJMP, SIZE);
    DWORD JMPSize = ((DWORD)newFunction - (DWORD)pOrigMBAddress - 5);
    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, 
                    PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy(oldBytes, pOrigMBAddress, SIZE);
    memcpy(&JMP[1], &JMPSize, 4);
    memcpy(pOrigMBAddress, JMP, SIZE);
    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, NULL);
}

int  WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uiType)
{
    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, myProtect, NULL);
    memcpy(pOrigMBAddress, oldBytes, SIZE);
    int retValue = MessageBoxW(hWnd, lpText, lpCaption, uiType);
    memcpy(pOrigMBAddress, JMP, SIZE);
    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, NULL);
    return retValue;
}
上面的代码就是标准API拦截的框架。这个DLL里包含的所有内容都会被注入到进程中。举个例子,拦截MessageBoxW这个函数。一旦DLL被注入,它(理论上)会获取MessageBoxW函数在user32.dll里的地址,然后拦截开始。在BeginRedirect函数中,有无条件相对转移指令JMP的操作码(0xE9),并且包含需要跳转的距离。反编译源代码后,指令看起来是这个样子:

JMP <distance>
RET
按下面的方法对BeginRedirect进行下修改可以提供更多有用的信息:

sprintf_s(debugBuffer, 128, "pOrigMBAddress: %x", pOrigMBAddress);
OutputDebugString(debugBuffer);
..
memcpy(oldBytes, pOrigMBAddress, SIZE);
sprintf_s(debugBuffer, 128, "Old bytes: %x%x%x%x%x", oldBytes[0], oldBytes[1],
    oldBytes[2], oldBytes[3], oldBytes[4], oldBytes[5]);
OutputDebugString(debugBuffer);
..
memcpy(&JMP[1], &JMPSize, 4);
sprintf_s(debugBuffer, 128, "JMP: %x%x%x%x%x", JMP[0], JMP[1],
    JMP[2], JMP[3], JMP[4], JMP[5]);
OutputDebugString(debugBuffer);

注入DLL后,可以打开DebugView工具进行查看:

可以从上图看出,在设置API钩子之前,从0x7E466534开始的MessageBoxW的代码流有下面5个字节:8B,FF,55,8B,EC。在使用memcpy(pOrigMBAddresss,JMP,SIZE)之后,代码流将会可以跳转到自定义函数的字节(E9,A7,AC,B9,91)。因此,每次位于0x7E466534地址的MessageBoxW被调用,程序会立即跳转到自定义函数的地址处。

int  WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uiType)
{
    OutputDebugString("In MyMessageBoxW");
//    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, myProtect, NULL);
//    memcpy(pOrigMBAddress, oldBytes, SIZE);
    int retValue = MessageBoxW(hWnd, lpText, lpCaption, uiType);
//    memcpy(pOrigMBAddress, JMP, SIZE);
//    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, NULL);
    return retValue;
}
如果你像上面的代码这样的话。当你注入DLL后,进程调用MessageBoxW,会发生下面的事情:


当你仔细看代码的时候发现,在自定义函数的内部调用了MessageBoxW并且返回一个值。但问题是,这里的MessageBoxW又重定向到你自定义的函数MyMessageBoxW中了。因此导致函数无限的递归调用。这就是为什么函数本身不能拦截自身的原因。为了能够调用原来的MessageBoxW函数,覆盖jump的字节需要写回去。然后调用原本的函数并返回结果。调用之后,再次重写跳转代码即可继续拦截。为了证明,我添加了下面这行:

MessageBoxW(NULL, L"This should pop up", L"Hooked MBW", MB_ICONEXCLAMATION);
在将原始字节写回内存后调用这个语句。我这里利用NotePad进行测试,当你搜索的文本不存在时,NotePad会弹出一个对话框。现在被注入DLL后的结果是:


效果如上。理论上说,譬如修改导入表的方法是比较好的,因为钩子可以保存起来,随时可以删除掉。

3.Detours API Hooking

微软的Detours库工作方式和上述的类似,看如下的概述:

“Detours是一个库,用于拦截x86机器上任意的win32二进制函数。拦截代码是在运行时动态使用的。Detours将目标函数开头的几条指令用指向用户提供的函数的跳转指令替代。目标函数的指令被放在一个地方,这个地方的地址放在一个目标指针里。Detour函数既可以替换目标函数,也可以通过在自定义函数内部调用目标函数指针来扩充目标函数。”

尽管上面的步骤非常的复杂并且抽象,但实际上,DetourAttach这个函数会完成所有的事情。

LONG DetourAttach(PVOID* ppPointer, PVOID pDetour);

这是负责拦截目标API的函数。第一个参数是一个指向被拦截函数的指针的指针。第二个参数是指向替换函数的指针。但是,在拦截开始之前,还需要做一些初始化的操作:

一个detour事务需要被初始化;

和这个事务相关的线程需要更新。

通过下面的调用即可实现上面的两步:

DetourTransactionBegin();

DetourUpdateThread(GetCurrentThread());

这两件事情做完后,detour开始attach。在attach之后,需要调用DetourTransactionCommit()来使detour生效。可以通过其返回值来判断是否拦截成功。

3.1例子:日志记录器

为了演示利用detours进行API拦截,我将写一段代码,去拦截Winsock模块里的send(...)和recv(...)两个函数。在这两个函数里,我会在交还控制权之前,将发送信息和接收信息的缓冲区的内容写到日志文件里。特别需要注意的事情是detour函数的调用规约和参数必须和被detour的函数完全一致。send函数的申明如下:

int send(
  __in  SOCKET s,
  __in  const char *buf,
  __in  int len,
  __in  int flags
);
因此,自定义的函数需要这样:

int WINAPI MySend(SOCKET s, const char* buf, int len, int flags);
这里还需要申明一个指向原函数的指针:

int (WINAPI *pSend)(SOCKET, const char*, int, int) = send;
这里,将函数指针=send是一种比较好的方式。我之后使用的,是先将指针置为NULL,然后使用DetourFindFunction()去定位地址。recv函数同理,因此:

int (WINAPI *pSend)(SOCKET s, const char* buf, int len, int flags) = send;
int WINAPI MySend(SOCKET s, const char* buf, int len, int flags);
int (WINAPI *pRecv)(SOCKET s, char* buf, int len, int flags) = recv;
int WINAPI MyRecv(SOCKET s, char* buf, int len, int flags);
现在为止,将被hook的函数和将被重定向的函数已经定义好了。下面,开始进行拦截:

INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
    switch(Reason)
    {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hDLL);
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourAttach(&(PVOID&)pSend, MySend);
            if(DetourTransactionCommit() == NO_ERROR)
                OutputDebugString("send() detoured successfully");
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourAttach(&(PVOID&)pRecv, MyRecv);
            if(DetourTransactionCommit() == NO_ERROR)
                OutputDebugString("recv() detoured successfully");
            break;
            .
因正如我之前介绍的,先初始化一个detour事务,再更新相应的线程,然后调用DetourAttach。在调用DetourTransactionCommit()之后可以根据相应的返回值进行验证是否成功。这里,被detour的函数需要以指针的指针进行传递,所以类型修饰为&(PVOID&)。当这些操作成功后,便会对send,recv函数进行拦截。为了及时的记录发送和接收的内容,我选择open,write,close来操作日志文件。当然,这并不一定是最好的方法。代码如下:

int WINAPI MySend(SOCKET s, const char* buf, int len, int flags)
{
    fopen_s(&pSendLogFile, "C:\\SendLog.txt", "a+");
    fprintf(pSendLogFile, "%s\n", buf);
    fclose(pSendLogFile);
    return pSend(s, buf, len, flags);
}

int WINAPI MyRecv(SOCKET s, char* buf, int len, int flags)
{
    fopen_s(&pRecvLogFile, "C:\\RecvLog.txt", "a+");
    fprintf(pRecvLogFile, "%s\n", buf);
    fclose(pRecvLogFile);
    return pRecv(s, buf, len, flags);
}
代码中的pRecvLogFilepSendLogFile的类型都是FILE*。我在“Internet Checkers”上测试了这个程序,两个文件都成功的捕获到理论数据。在detour函数中,一定要注意返回语句。与其他修改内存的方法不同,detours允许你将控制权返回给程序,只需简单通过地址调用原函数即可。如果你不需要阻塞被处理的API,则在做完你想做的事情后将控制权返回给原来的函数。否则,你可以返回一些有用的结果。

3.2 一个更复杂的例子:MSN消息框

为了展示API拦截的强大功能,我决定对上述的例子做些功能扩充,允许用户可以往活动的会话窗口发送MSN即时消息。

通过拦截MSN消息框使用的WSARecv函数,可以从数据包里解析邮件内容,并且记录聊天过程中活动的SOCKET的个数。我下面会开一个对话框(MSN:smarterchild@hotmail.com),将对话框接收到的数据包里的内容记录下来,类似下面的信息:

MSG smarterchild@hotmail.com 
-%20SmarterChild%20-%20*unicef%20contributing%20to%20charity 
137..MIME-Version: 1.0..Content-Type: text/plain; 
charset=UTF-8..X-MMS-IM-Format: FN=Courier%20New; 
EF=; CO=800000; CS=0; PF=22....:-D :-) :-)
上面的协议很简单,不需要通过逆向工程进行加解密。上面数据包里包含了发送者的邮件信息。一些文本标记,邮件内容。MSN消息协议在这里是完全文档化的。我需要里面的明文信息。在接收到的数据包里,检测某些属性。检测通过后,就可以开始解析邮件内容并且保持活动的会话。

if(strstr(lpBuffers->buf, "MSG ") != 0 && 
  (strstr(lpBuffers->buf, "MIME-Version") != 0 && 
   strstr(lpBuffers->buf, "X-MMS-IM-Format") != 0))
    ParseAndStoreEmail(socket, lpBuffers->buf);
lpBuffers是WSARecv/WSASend的参数,是一个LPWSABUF结构体。被解析后,邮件和活动的SOCKET就被保存在两个相同的数组中,这便于以后的匹配和更新。

vector<string>emailList;
vector<socket>sessionList;
The parsing and storing function works like this
void ParseAndStoreEmail(SOCKET session, const char* buffer)
{
    string email;
    int i = 4; //4 to skip "MSG " part
    while(buffer[i] != ' ')
    {
        email += buffer[i];
        i++;
    }
    if(SearchForDuplicates(session, email.c_str()) != -1)
    {
        emailList.push_back(email);
        sessionList.push_back(session);
        SendDlgItemMessage(::g_hDlg, IDC_CBUSERS, CB_ADDSTRING, NULL,
            (LPARAM)email.c_str());
        SendDlgItemMessage(::g_hDlg, IDC_CBUSERS, CB_SETCURSEL, emailList.size()-1, 0);
    }
}
由于这个函数只有在接收到数据包的时候被调用,因此为了解析出邮件地址,我选取MSG之后的第一个空格和下一个空间之前的内容。如果这个地址是之前向量中没有的,则该地址和活动的SOCKET会分别保存到相应的向量中。邮件地址之后会通过自定义的消息发送到组合框空间里进行显示:

case IDOK:
    {
        int index = SendDlgItemMessage(hDlg, IDC_CBUSERS, CB_GETCURSEL, 0, 0);
        int textLength = SendDlgItemMessage(hDlg, IDC_CHAT, WM_GETTEXTLENGTH, 0, 0);
        if(textLength == 0)
            break;
        char* emailSelected = new char[128];
        char* packet = new char[textLength+1];
        GetDlgItemText(hDlg, IDC_CHAT, packet, textLength+1);
        SendDlgItemMessage(hDlg, IDC_CBUSERS, CB_GETLBTEXT, index, (LPARAM)emailSelected);
        SOCKET sessionToSendTo = GetSessionFromEmail(emailSelected);
        BuildPacket(sessionToSendTo, packet);
        delete [] emailSelected;
        delete [] packet;
    }
    break;
上述代码是从组合框中获取正确的邮件地址和对应想SOCKET,然后创建并发送一个数据包。BuildPacket

代码如下:

void BuildPacket(SOCKET session, const char* message)
{
    char packetSize[8];
    ZeroMemory(packetSize, 8);
    string packetHeader = "MSG 10 N ";
    string packetSettings = "MIME-Version: 1.0\r\nContent-Type: " 
                            "text/plain; charset=UTF-8\r\n";
    packetSettings += "X-MMS-IM-Format: FN=MS%20Shell%20Dlg; " 
                      "EF=; CO=0; CS=0; PF=0\r\n\r\n";
    string packetMessage = message;
    int sizeOfPacket = packetSettings.length() + packetMessage.length();
    _itoa_s(sizeOfPacket, packetSize, 8, 10);
    packetHeader += packetSize;
    packetHeader += "\r\n";
    string fullPacket = packetHeader;
            fullPacket += packetSettings;
            fullPacket += packetMessage;
    psend(session, fullPacket.c_str(), fullPacket.length(), 0);
}
将被发送出去的MSN消息的大致结构如下:

MSG [Message #] [Acknowledge Flag] [Size of packet] [Text flags] [Message]
上述结构中,Message #对于数据包而言,是什么内容并不重要,因此可以当作数据包的一部分。而数据包大小这个字段非常重要,如果一个错误的大小被发送出去,session将会中断,然后重新建立一个新的SOCKET。对于文本的属性,我只是按正常的设置下即可。最终效果如下:


4.DLL注入

本文中我讨论了很多关于DLL注入的知识。API拦截属于DLL注入的一种,它是通过注入到目标进程的地址空间实现的。DLL注入是让目标进程加载我们自定义的DLL而实现的,有很多办法可以做到。很常见的一种方法是通过调用函数SetWindowsHookEx,给目标进程设置一个钩子。另一个常见的方法是调用CreateRemotethread函数。这个方法是我本文中将要介绍的,它不仅简单,而且有效。在开始拦截之前,需要找到将被注入的目标进程。

4.1 进程遍历

微软提供了一个强大的API进行进程遍历-CreateToolhelp32Snapshot,这个函数可以获取正在运行的进程,线程,堆等的快照。通过使用Process32First和Process32Next函数,可以遍历出每一个运行的进程。知道了这些,写代码就简单了:

#undef UNICODE
#include <vector>
#include <string>
#include <windows.h>
#include <Tlhelp32.h>
using std::vector;
using std::string;

int main(void)
{
    vector<string>processNames;
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hTool32 = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    BOOL bProcess = Process32First(hTool32, &pe32);
    if(bProcess == TRUE)
    {
        while((Process32Next(hTool32, &pe32)) == TRUE)
            processNames.push_back(pe32.szExeFile);
    }
    CloseHandle(hTool32);
    return 0;
}

我之所以没有记录Process32First的返回值,是因为返回的肯定是系统进程,因此没有必要去记录。Process32Next如果调用成功会返回TRUE。因此只需将 Process32Next放到一个循环里,将遍历到的进程的名字保存到一个向量中。当循环结束,每一个进程的名字都会保存在向量processNames中。那DLL注入是哪里进行呢?PROCESSENTRY32 结构有一个保存进程ID的变量。在循环中,当我们把进程名放入向量中的时候,我们开始注入DLL。

4.2 CreateRemoteThread

while((Process32Next(hTool32, &pe32)) == TRUE)
{
    processNames.push_back(pe32.szExeFile);
    if(strcmp(pe32.szExeFile, "notepad.exe") == 0)
    {
        char* DirPath = new char[MAX_PATH];
        char* FullPath = new char[MAX_PATH];
        GetCurrentDirectory(MAX_PATH, DirPath);
        sprintf_s(FullPath, MAX_PATH, "%s\\testdll.dll", DirPath);
        HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD    | PROCESS_VM_OPERATION    |
            PROCESS_VM_WRITE, FALSE, pe32.th32ProcessID);
        LPVOID LoadLibraryAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"),
            "LoadLibraryA");
        LPVOID LLParam = (LPVOID)VirtualAllocEx(hProcess, NULL, strlen(FullPath),
            MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
        WriteProcessMemory(hProcess, LLParam, FullPath, strlen(FullPath), NULL);
        CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryAddr,
            LLParam, NULL, NULL);
        CloseHandle(hProcess);
        delete [] DirPath;
        delete [] FullPath;
    }
}
上述代码中,先获取测试DLL“testdll.dll”的完整路径。然后打开需要注入的进程,这里要使用3个标记, PROCESS_ALL_ACCESS标记是最好的,但它要求将进程的权限修改为SE_DEBUG_ACCESS,限于篇幅,我没有这样做。进程被正确的打开后,通过GetProcAddress获取LoadLibraryA函数的地址。接下来需要在被注入的进程地址空间里,分配一些内存给DLL的路径。这一步做好后,就可以调用WriteProcessMemory将保存DLL的字符串写入到内存中,再然后就是调用CreateRemoteThread,将LoadLibraryA的开始地址和字符串当作参数传递进去。看下图,我们的DLL被目标进程加载起来了。


4.3 DetourCreateProcessWithDll

上面的技术,适应于注入正在运行的进程。如果是在一个进程运行之前注入呢?Detours库提供了一个DetourCreateProcessWithDll函数可以实现这个功能。使用这个函数相当于以CREATE_SUSPENDED 标记调用CreateProcess。这样进程被创建后,主线程会处于挂起状态。因此DLL可以再实际程序运行之前注入进去。一个重要的事情是被注入的DLL必须导出一个函数。如果没有,DetourCreateProcessWithDll函数会失败。下面是一个例子,将testdll.dll注入到notepad.exe里。

#undef UNICODE
#include <cstdio>
#include <windows.h>
#include <detours\detours.h>

int main()
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(STARTUPINFO));
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    si.cb = sizeof(STARTUPINFO);
    char* DirPath = new char[MAX_PATH];
    char* DLLPath = new char[MAX_PATH]; //testdll.dll
    char* DetourPath = new char[MAX_PATH]; //detoured.dll
    GetCurrentDirectory(MAX_PATH, DirPath);
    sprintf_s(DLLPath, MAX_PATH, "%s\\testdll.dll", DirPath);
    sprintf_s(DetourPath, MAX_PATH, "%s\\detoured.dll", DirPath);
    DetourCreateProcessWithDll(NULL, "C:\\windows\\notepad.exe", NULL,
        NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
        &si, &pi, DetourPath, DLLPath, NULL);
    delete [] DirPath;
    delete [] DLLPath;
    delete [] DetourPath;
    return 0;
}
DetourCreateProcessWithDll函数就是CreateProcessAPI的扩展版本,它包含了CreateProcess的所有参数,并需要额外的参数。一个是被注入的DLL的路径,另一个是detoured.dll的路径。这个过程就相当于,创建处于挂起状态的目标进程,注入DLL,然后调用ResumeThread使进程运行。

4.4 通过地址拦截
有时候,需要去拦截的函数不是标准的win32 API或者没有已知的导出函数。这就需要知道这个函数的固定地址。知道了这个函数的地址和参数后(通过逆向工程或者文档或者其他的手段),就可以拦截这个函数。下面的代码演示了具体过程:

#undef UNICODE
#include <cstdio>
#include <windows.h>
#include <detours\detours.h>

typedef void (WINAPI *pFunc)(DWORD);
void WINAPI MyFunc(DWORD);

pFunc FuncToDetour = (pFunc)(0x0100347C); //Set it at address to detour in
                    //the process
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
    switch(Reason)
    {
    case DLL_PROCESS_ATTACH:
        {
            DisableThreadLibraryCalls(hDLL);
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourAttach(&(PVOID&)FuncToDetour, MyFunc);
            DetourTransactionCommit();
        }
        break;
    case DLL_PROCESS_DETACH:
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)FuncToDetour, MyFunc);
        DetourTransactionCommit();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;
    }
    return TRUE;
}

void WINAPI MyFunc(DWORD someParameter)
{
    //Some magic can happen here
}
5.常见的错误

6.结束语

本文是讨论API拦截技术。还有很多方法可以实现API拦截,效率不一。读完本文后,能从Detours库的角度很容易的理解API拦截技术。通过使用Detours库,API拦截变得非常简单。因为内部过程诸如修改导入表,改变程序流等都已经被实现。这节省了大量的调试时间,可以允许程序员随意设置或移除钩子,而不必担心内存泄露。


简洁的语

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值