Detours使用说明

 

Detours使用说明

 

1 介绍... 1

2 Detours API hook. 1

2.1 hook DLL 中的函数... 2

2.2 hook自定义c 函数... 3

2.3 hook类成员函数... 4

2.4 DetourCreateProcessWithDll 5

2.5 Detouring by Address. 5

 

1 介绍

  Api hook包括两部分:api调用的截取和api函数的重定向。通过api hook可以修改函数的参数和返回值。关于原理的详细内容参见《windows核心编程》第19章和第22章。

 

2 Detours API hook

"Detours is a library for intercepting arbitrary Win32 binary functions on x86 machines. Interception code is applied dynamically at runtime. Detours replaces the first few instructions of the target function with an unconditional jump to the user-provided detour function. Instructions from the target function are placed in a trampoline. The address of the trampoline is placed in a target pointer. The detour function can either replace the target function, or extend its semantics by invoking the target function as a subroutine through the target pointer to the trampoline."

Detours库中,驱动detours执行的是函数 DetourAttach(…).

LONG DetourAttach(

    PVOID * ppPointer,

    PVOID pDetour

    );

这个函数的职责是挂接目标API,函数的第一个参数是一个指向将要被挂接函数地址的函数指针,第二个参数是指向实际运行的函数的指针,一般来说是我们定义的替代函数的地址。但是,在挂接开始之前,还有以下几件事需要完成:

  • 需要对detours进行初始化.
  • 需要更新进行detours的线程.

这些可以调用以下函数很容的做到:

  • DetourTransactionBegin()
  • DetourUpdateThread(GetCurrentThread())

在这两件事做完以后,detour函数才是真正地附着到目标函数上。在此之后,调用DetourTransactionCommit()detour函数起作用并检查函数的返回值判断是正确还是错误。

2.1 hook DLL 中的函数

在这个例子中,将要hook winsock中的函数 send(…)recv(…).在这些函数中,我将会在真正调用send或者recv函数前,把真正说要发送或者接收的消息写到一个日志文件中去。注意:我们自定义的替代函式一定要与被hook的函数具有相同的参数和返回值。例如,send函数的定义是这样的:

int send(

  __in  SOCKET s,

  __in  const char *buf,

  __in  int len,

  __in  int flags

);

因此,指向这个函数的指针看起来应该是这样的:

int (WINAPI *pSend)(SOCKET, const char*, int, int) = send;

把函数指针初始化成真正的函数地址是ok的;另外还有一种方式是把函数指针初始化为NULL,然后用函数DetourFindFunction(…) 指向真正的函式地址. send(…) 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的函数和重定向到的函数已经定义好了。这里使用 WINAPI 是因为这些函数是用 __stdcall 返回值的导出函数,现在开始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;

            .

            .

            .

它基本上是用上面介绍的步骤开始和结束 —— 初始化,更新detours线程,用DetourAttach(…)开始hook函数,最后调用DetourTransactionCommit() 函数, 当调用成功时返回 NO_ERROR, 失败是返回一些错误码.下面是我们的函数的实现,我发送和接收的信息写入到一个日志文件中:

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);

}

 

2.2 hook自定义c 函数

   举例来说明,假如有一个函数,其原型为

int RunCmd(const char* cmd);

如果要hook这个函数,可以按照以下几步来做:

a)     include 声明这个函数的头文件

b)     定义指向这个函数的函数指针,int (* RealRunCmd)(const char*) = RunCmd;

c)     定义detour函数,例如: int DetourRunCmd(const char*);

d)     实现detour函数,如:

Int DetourRunCmd(const char* cmd)

{

   //extend the function,add what you want :)

   Return RealRunCme(cmd);

}

这样就完成了hook RunCmd函数的定义,所需要的就是调用DetourAttack

    DetourTransactionBegin();

     DetourUpdateThread(GetCurrentThread());

     DetourAttach(&(PVOID&)RealRunCmd, DetourRunCmd);

     if(DetourTransactionCommit() == NO_ERROR)

     {

         //error

     }

2.3 hook类成员函数

   Hook类成员函数通过在static函数指针来实现

   还是举例说明,假如有个类定义如下:

class CData

{

public:

    CData(void);

    virtual ~CData(void);

    int Run(const char* cmd);

};

现在需要hook int CData::Run(const char*) 这个函数,可以按照以下几步:

a) 声明用于hook的类

class CDataHook

{

public:

    int DetourRun(const char* cmd);

    static int (CDataHook::* RealRun)(const char* cmd);

};

b) 初始化类中的static函数指针

     int (CDataHook::* CDataHook::RealRun)(const char* cmd) = (int (CDataHook::*)(const char*))&CData::Run;

c) 定义detour函数

   int CDataHook::DetourRun(const char* cmd)

{

    //添加任意你想添加的代码

    int iRet = (this->*RealRun)(cmd);

    return iRet;

}

e)     调用detourAttach函数

    DetourTransactionBegin();

    DetourUpdateThread(GetCurrentThread());

    DetourAttach(&(PVOID&)CDataHook::RealRun, (PVOID)(&(PVOID&)CDataHook::DetourRun));

    if(DetourTransactionCommit() == NO_ERROR)

    {

        //error

    }

 

2.4 DetourCreateProcessWithDll

使用这个函数相当于用CREATE_SUSPENDED 标志调用函数CreateProcess. 新进程的主线程处于暂停状态,因此DLL能在函数被运行钱被注入。注意:被注入的DLL最少要有一个导出函数. 如用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;
}

2.5 Detouring by Address

假如出现这种情况怎么办?我们需要hook的函数既不是一个标准的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
}

 

  • 0
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Detours是微软开发的一个函数库, 用于修改运行中的程序在内存中的影像,从而即使没有源代码也能改变程序的行为。具体用途是: 拦截WIN32 API调用,将其引导到自己的子程序,从而实现WIN32 API的定制。 为一个已在运行的进程创建一新线程,装入自己的代码并运行。 ---- 本文将简介Detours的原理,Detours库函数的用法, 并利用Detours库函数在Windows NT上编写了一个程序,该程序能使有“调试程序”的用户权限的用户成为系统管理员,附录利用Detours库函数修改该程序使普通用户即可成为系统管理员 (在NT4 SP3上)。 一. Detours的原理 ---- 1. WIN32进程的内存管理 ---- 总所周知,WINDOWS NT实现了虚拟存储器,每一WIN32进程拥有4GB的虚存空间, 关于WIN32进程的虚存结构及其操作的具体细节请参阅WIN32 API手册, 以下仅指出与Detours相关的几点: ---- (1) 进程要执行的指令也放在虚存空间中 ---- (2) 可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行,再改写其内容,从而修改正在运行的程序 ---- (3) 可以使用VirtualAllocEx从一个进程为另一正运行的进程分配虚存,再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行,并把要执行的指令以二进制机器码的形式写入,从而为一个正在运行的进程注入任意的代码 ---- 2. 拦截WIN32 API的原理 ---- Detours定义了三个概念: ---- (1) Target函数:要拦截的函数,通常为Windows的API。 ---- (2) Trampoline函数:Target函数的复制品。因为Detours将会改写Target函数,所以先把Target函数复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。 ---- (3) Detour 函数:用来替代Target函数的函数。 ---- Detours在Target函数的开头加入JMP Address_of_ Detour_ Function指令(共5个字节)把对Target函数的调用引导到自己的Detour函数, 把Target函数的开头的5个字节加上JMP Address_of_ Target _ Function+5作为Trampoline函数。例子如下: 拦截前:Target _ Function: ;Target函数入口,以下为假想的常见的子程序入口代码 push ebp mov ebp, esp push eax push ebx Trampoline: ;以下是Target函数的继续部分 …… 拦截后: Target _ Function: jmp Detour_Function Trampoline: ;以下是Target函数的继续部分 …… Trampoline_Function: ; Trampoline函数入口, 开头的5个字节与Target函数相同 push ebp mov ebp, esp push eax push ebx ;跳回去继续执行Target函数 jmp Target_Function+5 ---- 3. 为一个已在运行的进程装入一个DLL ---- 以下是其步骤: ---- (1) 创建一个ThreadFuction,内容仅是调用LoadLibrary。 ---- (2) 用VirtualAllocEx为一个已在运行的进程分配一片虚存,并把权限更改为可读可写可执行。 ---- (3) 把ThreadFuction的二进制机器码写入这片虚存。 ---- (4) 用CreateRemoteThread在该进程上创建一个线程,传入前面分配的虚存的起始地址作为线程函数的地址,即可为一个已在运行的进程装入一个DLL。通过DllMain 即可在一个已在运行的进程中运行自己的代码。 二. Detours库函数的用法 ---- 因为Detours软件包并没有附带帮助文件,以下接口仅从剖析源代码得出。 ---- 1. PBYTE WINAPI DetourFindFunction(PCHAR pszModule, PCHAR pszFunction) ---- 功能:从一DLL中找出一函数的入口地址 ---- 参数:pszModule是DLL名,pszFunction是函数名。 ---- 返回:名为pszModule的DLL的名为pszFunction的函数的入口地址 ---- 说明:DetourFindFunctio

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值