小弟最近做一个项目,需要对程序本身进行APIHOOK, 如何区分hook到的函数属于哪一个DLL??下面是问题的详细描述:进程P1运行的时候会加载DLL1,DLL2,DLL3...等多个Dll模块,这几个DLL里面都有SetPriorityClass这个API调用,我用APIHOOK拦截了SetPriorityClass这个API调用,并重写了这个函数:My_SetPriorityClass(HANDLE hProcess,
  DWORD dwPriorityClass)
{
   ....  //问题是:我在这里怎么区分是哪一个DLL调用了这个函数??
}小弟已经在这个问题上纠缠了好几天,一直无法解决,大家如果有这方面研究的,多提提意见! 先谢谢了, 我用的微软的Detours做的,不知道这个Detour在win98,xp,win2003上能不能用,帮组文件上说只能2000才能用?

解决方案 »

  1.   

    hook时好象有个句柄的,但好久没用了,还是帮你顶,看看别人的高见吧.
      

  2.   

    挑战性???一点也没……没用过DETOURS,只用过改输入表和直接改地址两种方法。要HOOK的函数,就得先得到那个DLL的句柄。(不是调用的DLL,输出这个函数的DLL)没有那个DLL的句柄也就HOOK不到其中的函数。如果你已经HOOK到,说明你已经找到那个DLL的。
      

  3.   

    to rabo(不哭死人) : 
    我是建立了一个新的dll文件,在dllmain函数中调用了:
    if (dwReason==DLL_PROCESS_ATTACH)
    TrampolineInstall();
    else if (dwReason == DLL_PROCESS_DETACH)
    TrampolineUnInstall();
    然后将这个dll文件通过loadlibrary加载到我的进程中, 现在我能hook到这个函数,但是无法分辨出是哪一个dll被拦截了,dll的句柄之前我也不知道。to  DentistryDoctor(雅克医生<改行做程序员了>) :
    你说的“通过调用堆栈。”,是什么意思,能详细点吗?
      

  4.   

    我是低手。
    DLL1,DLL2,DLL3...等多个Dll模块都是你写的吗?如果是的话,每次调用SetPriorityClass这个API时
    先将一个全局变量设置一个标志值,用以标志此 DLL ,调用完了将其清空。如果是多线程的就比较麻烦了。
    方法老土了点,不知行不行。
      

  5.   

    detour是针对2进制扩展的, 就是因为没有原代码才选用detour来做
      

  6.   

    LPVOID * p = (LPVOID *) & hProcess; LPVOID p1 = p[-1];
        
    TCHAR address[MAX_PATH + 1]; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(p1, & mbi, sizeof(mbi)); GetModuleFileName((HMODULE) mbi.AllocationBase, address, MAX_PATH); wsprintf(address + _tcslen(address), "+0x%x", (LPBYTE)p1 - (LPBYTE)mbi.AllocationBase);csdn.fengyuan.com
      

  7.   

    你是不是要这个?HMODULE ModuleFromAddress(LPVOID lpv)
    {
    MEMORY_BASIC_INFORMATION mbi;
    memset(&mbi, 0, sizeof(mbi));
    return VirtualQuery(lpv, &mbi, sizeof(mbi)) ? (HMODULE)mbi.AllocationBase : NULL;
    }
      

  8.   

    通过调用堆栈获取调用地址,然后计算在那个Dll模块中。
      

  9.   

    谢谢 FengYuanMSFT(袁峰 www.fengyuan.com) 和 cctime() , 我马上试
      

  10.   

    to FengYuanMSFT(袁峰 www.fengyuan.com) :非常感谢你的帮助,以前经常看到你在程序人生和技术版块帮其他朋友回答问题,对你非常佩服。 今天很高兴能得到你的帮助,我试了你的代码,有一点问题,不知道是什么问题LPVOID p1 = p[-1];//这句无法获得,p1为0xcccccccc如果你有时间,能否适当解释一下
        
      

  11.   

    to cctime() :
    非常感谢你的帮助, 你的代码工作正常,不过我要获得所要hook的api未于哪一个dll,无法知道LPVOID lpv
      

  12.   

    > LPVOID * p = (LPVOID *) & hProcess;
    > LPVOID p1 = p[-1];If hProcess is the first parameter of the routine, then p[-1] should be return address. The reason is that C function is compiled into:   push parameter n
       ...
       push parameter 2
       push parameter 1
       call functionCheck under assembly code to see if hProcess is really the first paramter (pushed last in order). If not, adjust the code accordingly.
      

  13.   

    //引用你的代码
    My_SetPriorityClass(HANDLE hProcess,
      DWORD dwPriorityClass)
    {
       ....  //问题是:我在这里怎么区分是哪一个DLL调用了这个函数??
       //该函数的返回地址在调用者DLL里吧,用这个返回地址做为参数!
       //不过取返回地址不是很容易哦,熟悉汇编的应该比较清楚。
    }
      

  14.   

    to FengYuanMSFT(袁峰 www.fengyuan.com) :
    谢谢!你说的我基本上明白了,但是如果我要hook 的api没有hprocess这个参数怎么办呢?比如要hook的是一个没有参数的函数,我就无法获得这个地址to cctime() :
    谢谢!我是这样实现的:
    My_SetPriorityClass(HANDLE hProcess,
      DWORD dwPriorityClass)
    {
        //对调用该函数的dll进行判断
        ....    return Real_SetPriorityClass(hProcess,dwPriorityClass);
     }
    如果我用真实的SetPriorityClass地址作为参数,应该得到kernel32.dll,而我想知道这次调用是通过用户的哪个dll完成的(DLL1或者DLL2 ),  这里边好象没有可以利用的地址
      

  15.   

    网络中断了大半天,来晚了。//谁调用了我
    //以函数返回地址为基础,确定调用此函数的module所在文件的路径。(应该有一定的通用性)
    //以《Windows 核心编程》一书中提供的CAPIHook类为基础进行拦截试验,
    //在替换函数中添加以下代码得到被替换函数所属DLL文件的路径---------------TCHAR str[128];//被替换函数所属DLL文件的全路经
    DWORD dw;//替换函数执行完后将执行的下一条语句的地址,或者说替换函数的返回地址。
    __asm
    {
    push eax;
    mov eax,[ebp+4];
    mov dw,eax;//替换函数的返回地址
    pop eax;
    }
    HMODULE hmod=0;
    MEMORY_BASIC_INFORMATION  mbi;
    if(VirtualQuery((void*)dw,&mbi,sizeof(mbi)))
    {
    if(mbi.AllocationBase )
    {
    hmod=(HMODULE)mbi.AllocationBase;
    ::GetModuleFileName(hmod,str ,128 );
    AfxMessageBox(str,MB_OK,0);
    }
    }//str 0x0011d9d8 "E:\VC Project\我的实验室\easyLock\Debug\km.dll" char [128]
    //str 0x0011dad8 "E:\Microsoft Visual Studio .NET 2003\Common7\Tools\SPYXXHK.DLL" char [128]
    //------------------------------------------------------------------------CAPIHook类通过替换module对函数地址的引用达到拦截的目的,替换函数与被拦截函数不在同一个module地址空间(从一个module地址空间跳转到另一个module地址空间)。
    我没用过detours,不了解他的工作机制。如果替换函数与被拦截函数同在一个module地址空间,使用以下代码就行了。
    hmod=AfxGetInstanceHandle();
    ::GetModuleFileName(hmod,str ,128 );
    另外,我调试拦截的是CallNextHookEx函数,位于User32.dll;SetPriorityClass位于Kernel32.dll,感觉这个应该不是什么问题。吧《Windows 核心编程》还是没看明白,八九行代码,查书、调试折腾了四五个小时,搞的一晚上没睡。while,希望别再有人说我无聊。
      

  16.   

    to yafizyh(亚斐) :
    谢谢!我调试了你的代码,
    DWORD dw;
    __asm
    {
    push eax;
    mov eax,[ebp+4];//
    mov dw,eax;
    pop eax;
    }
    dw中所指向的页面地址依然是属于My_SetPriorityClass所在的模块,而不是用户模块页面地址。比如,我的apihook.dll中包含了My_SetPriorityClass的定义,上面代码得到的模块路径是apihook.dll,而不是完成这次调用的DLL1或者DLL2
      

  17.   

    我就没办法帮你调试了,这样想,现在ebp所指向堆栈位置存放的是上一个调用的ebp的值。以此类推,向上查找,看看到底是谁调用的子程序。
      

  18.   

    该不会是jmp执行的跳转吧:(
      

  19.   

    楼上的,先谢了! 我在试下,主要是apihook的dll没有办法单步跟踪调试,所以很麻烦这是detour中的定义:
    #define DETOUR_TRAMPOLINE_SIZE          32
    #define DETOUR_SECTION_HEADER_SIGNATURE 0x00727444   // "Dtr\0"#define DETOUR_TRAMPOLINE(trampoline,target) \
    static PVOID __fastcall _Detours_GetVA_##target(VOID) \
    { \
        return &target; \
    } \
    \
    __declspec(naked) trampoline \
    { \
        __asm { nop };\
        __asm { nop };\
        __asm { call _Detours_GetVA_##target };\
        __asm { jmp eax };\
        __asm { ret };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
        __asm { nop };\
    }不知道是不是用的jmp指令,我汇编不太熟悉,不太明白detour的原理
      

  20.   

    我这里有detour的原代码,要不我贴上来
      

  21.   

    我也是才开始学,大规模的代码我也看不懂啊。
    VC可以单步跟踪吧,不行的话用反汇编软件试一下。如果都不行,换一种hook方法吧。
      

  22.   

    也来提供一种思路,
    那些DLL的HMODULE你总可以取得吧,
    win98/2000下用ToolHelp函数 Module32First,Module32Next
    NT4.0下用PSAPI EnumProcessModules假设你的取得的地址为 hModulePIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS32 lpNtHeader = 
     (PIMAGE_NT_HEADERS32)((char *)hModule + lpDosHeader->e_lfanew);
    DWORD dwImageSize = lpNtHeader->OptionalHeader.SizeOfImage这样你就取得了模块的起始地址(hModule)和模块的大小
    (以上代码需要了解一下PE文件格式)Module Header = hModule
    Module Tail = hModule + dwImageSize (代码和静态数据)然后再你的挂接函数中这样写:
    BOOL WINAPI My_SetPriorityClass(HANDLE hProcess,DWORD dwPriorityClass){
        //定义一个变量,存储函数的返回地址
        //当某个DLL调用这个函数的时候,那么该函数的返回地址肯定在这个DLL内
        //也就是说  hModule < dwReturnAddress < hModule + dwImageSize    DWORD dwReturnAddress;    //接下来的工作就是取得这个返回地址了
        //在__stdcall(WINAPI)的调用中规则中
        //向堆栈中压入参数的顺序是这样的
        //Return Address,dwPriorityClass,hProcess
        //所以进入函数后,堆栈中的顺序该为
        //[ESP] hProcess
        //[ESP + 4] dwPriorityClass
        //[ESP + 8] Return Address
        //但你如果用VC编译后,编译器通常会在进入函数后插入这样的代码
        //PUSH  EBP    (保存寄存器 EBP)
        //MOV   EBP,ESP (将栈指针赋予 EBP,用EBP于访问堆栈中的参数)
        //SUB   ESP,XXXX (这就是用于分配临时变量了)
        //所以这时候,堆栈将变成这样
        //[EBP]   EBP 原始值
        //[EBP + 4] hProcess
        //[EBP + 8] dwPriorityClass
        //[EBP + 12] Return Address
        //这样,代码就该写成如下
        
        _asm{
             MOV  EAX,DWORD PTR [EBP + 12]
             MOV  dwReturnAddress,EAX
        }
        
       //这样,就取得了函数的返回地址.
       //就可以根据hModule < dwReturnAddress < hModule + dwImageSize
       //来判断是哪个DLL调用了该函数了
    }当然,可能VC的编译结果中还会用其他的入栈操作,你可以跟踪一下.
    以确定返回地址在堆栈中的确切位置.
    或着按楼上的一种写法,这样,编译器是不会插入其他任何代码的.
    当然,相应的其他操作你也得自己写了.__declspec(naked) BOOL WINAPI My_SetPriorityClass(HANDLE hProcess,DWORD dwPriorityClass)
    {   
         DWORD dwReturnAddress;
         BOOL  blnRet;    _asm{
            PUSH  EBP
            MOV   EBP,ESP
            SUB   ESP,8      //分配dwReturnAddress,blnRet变量(两个双字 4 * 2)
            MOV   EAX,DWORD PTR [EBP + 12]
            MOV   dwReturnAddress,EAX
        }
        ...
        blnRet = lpOldSetPriorityClass(hProcess,dwPriorityClass);
        ...
       _asm{
           MOV    EAX,blnRet   //设置返回值
           ADD    ESP,8        //恢复 ESP,EBP 
           MOV    ESP,EBP
           POP    EBP
           RET    8           //调整堆栈并返回(参数的两个双字)
       }
    }
      

  23.   

    xlt123(杀了你好吗),非常感谢你!  我仔细看了你的代码, 并调试通过在My_SetPriorityClass中,
    通过下面代码:
     _asm{
    push eax;
    mov eax,[ebp+8];
    mov dw,eax;//替换函数的返回地址
    pop eax;
        }
    可以获得模块的返回地址。 证明了在detour中模块返回地址应该放在[edp+参数数量*4]里面,
    [EBP] 参数1地址
    [EBP + 1*4] 参数2地址
    [EBP + 2*4] 参数3地址
    ...
    [EBP + n*4-n] 参数n返回地址
    [EBP + n*4] 返回地址
    至于它为什么没有在函数开始的时候对EBP 原始值做push 操作,还不是很明白。不过总算结果正确了
      

  24.   

    在高级语言的编译结果中
    PUSH  EBP
    MOV   EBP,ESP
    SUB   ESP,XXXX
    三条指令是最常见的,
    保存EBP,主要是为了函数的嵌套调用和递归.
    仔细想一想,在一个函数中使用EBP来访问栈(参数和临时变量),
    如果在某处调用了一个子函数,按照一个通用规则,这个子函数也用
    EBP来访问栈,如果在子函数中不保护EBP的话,那么函数返回后,EBP就
    遭到了破坏,以后再用EBP访问栈就发生错误了.是不是这个理?
      

  25.   

    //在__stdcall(WINAPI)的调用中规则中
     //向堆栈中压入参数的顺序是这样的
     //Return Address,dwPriorityClass,hProcess在汇编中调用call指令会将函数返回地址压入堆栈,并且函数地址装入eip,此时函数以执行。所以应该先压参数,再调用call。也就是说,堆栈中压入参数的顺序:dwPriorityClass
    hProcess
    Return Address
    ebp  通常情况下在My_SetPriorityClass中,通过下面代码:
     _asm{
    push eax;
    mov eax,[ebp+8];
    mov dw,eax;
    pop eax;
        }
    在eax中存放的是函数的第一个参数的值。就此例而言应该为hProcess的值。
      

  26.   

    你直接用hProcess的值代替试一下。看对不对,应为detour中直接使用了汇编,不知道是否遵循常规的调用规则。
      

  27.   

    我调试后发现,使用hProcess是正确的压栈顺序为:
    未知1->参数2->参数1->未知2
    我通过未知1和未知2的地址都无法正确得到对应的dll名称,不知道是怎么回事?
    通过未知1的地址我得到了kernel32.dll,根据未知2的地址我得到本进程XXX.exe的路径
    非常感谢yafizyh(亚斐)
      

  28.   

    想不明白,怎么会是hProcess。莫名其妙@#&%&*%@&*%$#@!???啊那位大虾能解释一下。
      

  29.   

    到底有结果了没有?能用 ModuleFromAddress(函数返回地址) 在假API函数里获知正被哪个DLL调用了吗?
      

  30.   

    还是不行, 后来改了hook方式