小弟最近做一个项目,需要对程序本身进行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才能用?
DWORD dwPriorityClass)
{
.... //问题是:我在这里怎么区分是哪一个DLL调用了这个函数??
}小弟已经在这个问题上纠缠了好几天,一直无法解决,大家如果有这方面研究的,多提提意见! 先谢谢了, 我用的微软的Detours做的,不知道这个Detour在win98,xp,win2003上能不能用,帮组文件上说只能2000才能用?
解决方案 »
- 【求助】关于TXT读取问题,谢谢大家了
- ADO编程问题!!
- 请问有没有人用过CComPort类?
- WM_SETTINGCHANGE是如何产生的啊?
- VC中,跟踪STL类型的变量,有什么好方法?
- MFC的SDI程序,Active X 问题?在线。。。。。
- CMapStringToString::RemoveKey的问题
- 简单问题关于LPTSTR和CString
- 哪位高人有没有做过这个东西? 等待ing
- 有谁知道用SQLTables()函数返回数据库中的表后,怎么再从该返回结果中取出该数据库中表的名称!谢谢了!!
- windowsSDK编程设备坐标与逻辑坐标的问题
- 为什么我的程序总提示缺少msvcr71.dll和atl71.dll
我是建立了一个新的dll文件,在dllmain函数中调用了:
if (dwReason==DLL_PROCESS_ATTACH)
TrampolineInstall();
else if (dwReason == DLL_PROCESS_DETACH)
TrampolineUnInstall();
然后将这个dll文件通过loadlibrary加载到我的进程中, 现在我能hook到这个函数,但是无法分辨出是哪一个dll被拦截了,dll的句柄之前我也不知道。to DentistryDoctor(雅克医生<改行做程序员了>) :
你说的“通过调用堆栈。”,是什么意思,能详细点吗?
DLL1,DLL2,DLL3...等多个Dll模块都是你写的吗?如果是的话,每次调用SetPriorityClass这个API时
先将一个全局变量设置一个标志值,用以标志此 DLL ,调用完了将其清空。如果是多线程的就比较麻烦了。
方法老土了点,不知行不行。
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
{
MEMORY_BASIC_INFORMATION mbi;
memset(&mbi, 0, sizeof(mbi));
return VirtualQuery(lpv, &mbi, sizeof(mbi)) ? (HMODULE)mbi.AllocationBase : NULL;
}
非常感谢你的帮助, 你的代码工作正常,不过我要获得所要hook的api未于哪一个dll,无法知道LPVOID lpv
> 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.
My_SetPriorityClass(HANDLE hProcess,
DWORD dwPriorityClass)
{
.... //问题是:我在这里怎么区分是哪一个DLL调用了这个函数??
//该函数的返回地址在调用者DLL里吧,用这个返回地址做为参数!
//不过取返回地址不是很容易哦,熟悉汇编的应该比较清楚。
}
谢谢!你说的我基本上明白了,但是如果我要hook 的api没有hprocess这个参数怎么办呢?比如要hook的是一个没有参数的函数,我就无法获得这个地址to cctime() :
谢谢!我是这样实现的:
My_SetPriorityClass(HANDLE hProcess,
DWORD dwPriorityClass)
{
//对调用该函数的dll进行判断
.... return Real_SetPriorityClass(hProcess,dwPriorityClass);
}
如果我用真实的SetPriorityClass地址作为参数,应该得到kernel32.dll,而我想知道这次调用是通过用户的哪个dll完成的(DLL1或者DLL2 ), 这里边好象没有可以利用的地址
//以函数返回地址为基础,确定调用此函数的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,希望别再有人说我无聊。
谢谢!我调试了你的代码,
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
#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 ⌖ \
} \
\
__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的原理
VC可以单步跟踪吧,不行的话用反汇编软件试一下。如果都不行,换一种hook方法吧。
那些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 //调整堆栈并返回(参数的两个双字)
}
}
通过下面代码:
_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 操作,还不是很明白。不过总算结果正确了
PUSH EBP
MOV EBP,ESP
SUB ESP,XXXX
三条指令是最常见的,
保存EBP,主要是为了函数的嵌套调用和递归.
仔细想一想,在一个函数中使用EBP来访问栈(参数和临时变量),
如果在某处调用了一个子函数,按照一个通用规则,这个子函数也用
EBP来访问栈,如果在子函数中不保护EBP的话,那么函数返回后,EBP就
遭到了破坏,以后再用EBP访问栈就发生错误了.是不是这个理?
//向堆栈中压入参数的顺序是这样的
//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的值。
未知1->参数2->参数1->未知2
我通过未知1和未知2的地址都无法正确得到对应的dll名称,不知道是怎么回事?
通过未知1的地址我得到了kernel32.dll,根据未知2的地址我得到本进程XXX.exe的路径
非常感谢yafizyh(亚斐)