我想截获魔兽程序中的一个API函数,用到WriteProcessMemory函数将原API函数地址改写为我自己的函数地址,代码如下:
if ( VirtualProtectEx(GetCurrentProcess(), ppfn,sizeof(pfnNew), PAGE_EXECUTE_READWRITE, &oldProtect) )
{
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,sizeof(pfnNew), NULL);
VirtualProtectEx(GetCurrentProcess(), ppfn,sizeof(pfnNew), oldProtect, NULL);
}
ppfn为原函数地址,pfnNew为我自定义函数地址,改写之后,运行魔兽发现,截获API成功(因为我自定义的函数里的内容运行了),但问题是,魔兽会弹出一个错误,说什么什么内存不可读,我就纳闷了,我已经将魔兽进程中的内存页权限更改过啊,而且也是更改成功的,为什么会发生这个问题呢?请大家帮我想想问题的原因吧,多谢了!
if ( VirtualProtectEx(GetCurrentProcess(), ppfn,sizeof(pfnNew), PAGE_EXECUTE_READWRITE, &oldProtect) )
{
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,sizeof(pfnNew), NULL);
VirtualProtectEx(GetCurrentProcess(), ppfn,sizeof(pfnNew), oldProtect, NULL);
}
ppfn为原函数地址,pfnNew为我自定义函数地址,改写之后,运行魔兽发现,截获API成功(因为我自定义的函数里的内容运行了),但问题是,魔兽会弹出一个错误,说什么什么内存不可读,我就纳闷了,我已经将魔兽进程中的内存页权限更改过啊,而且也是更改成功的,为什么会发生这个问题呢?请大家帮我想想问题的原因吧,多谢了!
说明你写成功了的,看看是不是第二个 VirtualProtectEx 引起的,先把它注释掉
This application has encountered a critical error: FATAL ERROR! Program: F:\Warcraft III\War3.exe
Exception: 0xC0000005 (ACCESS_VIOLATION) at 001B:6F02D78C The instruction at '0x6F02D78C' referenced memory at '0x00000010'.
The memory could not be 'read'.
我上网查了一下,大家也帮我看看这个网页里说的跟我这个有关系没?
http://zhidao.baidu.com/question/15582845.htmlTo Meteor_Code:
因为我的代码是在动态库里hook到魔兽进程的,所以应该是在同一进程中,应该不会是这个问题。To splei1 && yangzhe:
我并没改数据啊, 我拦截的是sendto函数,我的函数原型是
int WINAPI MySendTo( SOCKET s,const char* buf,int len,int flags,const struct sockaddr* to,int tolen)
{
return sendto(s,buf,len,flags,to,tolen);
}
请大家再帮我想想问题原因吧,一旦解决问题,定重分相赠!
其中这一句:WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,sizeof(pfnNew), NULL); 我认为第三个参数是不对的,它的原形本来是:
LPVOID lpBuffer,//Pointer to buffer to write data to.从变量名字命名来看,lpfnNew一定是一个指向新函数的起始地址指针变量吧.而你传入是pfnNew这个变量的地址,而不是你自定义的函数的地址.能明白吗?不要有取地址符.看了你上面提供的那个网页,好像他是装完程序就有这个错误提示,并没有写程序对其虚拟空间进行修改.我没玩过魔兽,这个错误在正常装完游戏程序后也会出现吗?呵呵你再改改试试吧,提供的信息太少,而出错的原因太多,实在不能一下子就改对了.你也可以用OD反汇编一下,看看到底怎么回事.
看LZ代码 pfnNew应该是个函数指针 sizeof指针 肯定是返回4的 而你HOOK 函数 最少也要写5字节吧 这么明显的错误 竟然还看不出来... CSDN 的星级别水份确实很大
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,sizeof(pfnNew), NULL); 这句应该是没问题的,这参考了《Windows核心编程》中的相关内容,书中范例也是如此使用的。to IORI915189 :
你说的HOOK函数,最少5字节应该是用jmp汇编指令跳转的方法实现的HOOK API的方法,而我这里不需要跳转,直接是将源地址更改,如原函数地址用四字节存储,我只需要将这四字节的内容改变,不知道我这样理解是否有误?我也是最近初学HOOK API的,本想将所学的皮毛试验一下,没想到就出这种问题,很是受打击,希望大家再帮我想想办法!多谢
5字节是不对的为什么?我还说你有什么想法呢 原来就是照样抄的核心编程,核心编程里都谈到了 这样写部分api无效,估计你连为啥无效你都不懂吧 我免费教教你好了 你这样把入口的函数修改5个字节然后跳回去 需要汇编代码的配合,要刚好5个字节是一个完整的汇编代码段 比如像messagebox createprocess这些函数入口是 77D5050B > 8BFF mov edi, edi ; ntdll.7C930738
77D5050D 55 push ebp
77D5050E 8BEC mov ebp, esp 这里的汇编代码是8BFF558BEC,刚好5个字节,那么这样做是可以的。 但是并不是所有的api函数的入口都是这种模式的 不信的话可以hook getlasterror看看,你用你的方法能成功么? 当然是不能, 为什么不能呢?因为getlasterror的函数入口和前面谈到的函数不同,如果把他的代码以5个字节截取的话 会把他的入口代码截断,造成无法使用 7C930331 > 64:A1 18000000 mov eax, dword ptr fs:[18]
7C930337 8B40 34 mov eax, dword ptr [eax+34] 5字节的话64:A1 18000截走
剩下的是000
8b40 34
这样的代码能运行?能完成原先api的任务?
所以,很清楚的就知道,只要是api的入口不是
mov edi, edi
push ebp
mov ebp, esp
你的方法就不行 不幸的是,很多核心层的代码都吧是这样的入口 所以看完你的代码我就知道了 你其实也就hook过用户态的api,什么ssdt hook啥的你就知道一名。 麻烦你不懂不要装懂好不好 害人害己啊!
你也不说明白... 直接修改4字节地址 替换原地址的是 程序导入表HOOK 这样的话 上面的代码是没问题的 如果出错 就是你的接管函数的问题了
这可能性也太多了吧.
1.你要写的内存地址本身不对.例如要hook的API定位地址没对,这样写入的数据自然会出问题.
2.ppfn,pfnNew是啥类型的也没写出来,就不定写入的字节数不对.
.......你最好还是给出完整的某段核心代码,不然单这几句代码,这也太难推算出是啥问题了.
1:hook模块
LRESULT __stdcall CALLBACK ShellProc(int nCode,WPARAM wParam,LPARAM lParam)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (nCode == HSHELL_LANGUAGE)
{
HWND hwd = GetForegroundWindow();
if (hwd != NULL)
{
CWnd* pwd = CWnd::FromHandle(hwd);
CString s = _T("");
pwd->GetWindowText(s);
if (s == "Warcraft III")
{
PROC pfnOrig = GetProcAddress(::LoadLibrary("wsock32"),"sendto");
HMODULE hmod = GetModuleHandle("War3.exe");
if (hmod != NULL)
{
ReplaceIATEntryInOneMod("wsock32.dll",pfnOrig,(PROC)MySendTo,hmod);
}
else
{
::WritePrivateProfileString("引用动态库","句柄值","NULL","D:\\1.ini");
}
}
}
}
return CallNextHookEx( hkb2, nCode, wParam, lParam );
}
2:函数拦截模块
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller)
{
ULONG ulSize;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
ImageDirectoryEntryToData(hmodCaller, TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize); if(pImportDesc == NULL)
{
return;
} for(int i = 0; pImportDesc->Name; pImportDesc++, i++)
{
PSTR pszModName = (PSTR)((PBYTE) hmodCaller + pImportDesc->Name);
if(lstrcmpiA(pszModName, pszCalleeModName) == 0)
break;
} if(pImportDesc->Name == 0)
return; PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE) hmodCaller + pImportDesc->FirstThunk); for(int i = 0; pThunk->u1.Function; pThunk++, i++)
{
PROC* ppfn = (PROC*) &pThunk->u1.Function;
BOOL fFound = (*ppfn == pfnCurrent);
if(fFound)
{
::WritePrivateProfileString("截获sendto","有这个函数吗","Yes","D:\\1.ini");
DWORD oldProtect;
if ( VirtualProtectEx(GetCurrentProcess(), ppfn,sizeof(pfnNew), PAGE_EXECUTE_READWRITE, &oldProtect) )
{
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,sizeof(pfnNew), NULL);
VirtualProtectEx(GetCurrentProcess(), ppfn,sizeof(pfnNew), oldProtect, NULL);
}
break;
}
}
}
3:自定义函数模块
int WINAPI MySendTo( SOCKET s,const char* buf,int len,int flags,const struct sockaddr* to,int tolen)
{
::WritePrivateProfileString("截获sendto","截获成功吗","Yes","D:\\3.ini");
return sendto(s,buf,len,flags,to,tolen);
}
以上就是最主要的三个模块,现在的运行效果就是,D:\\3.ini文件中已成功写入“截获成功吗:Yes",我想这说明我的自定义函数已经起到作用了,但现在关键就是出现我在5楼描述的错误,请高手指正!!!
{
::WritePrivateProfileString("截获sendto","截获成功吗","Yes","D:\\3.ini");
return sendto(s,buf,len,flags,to,tolen);
}
你这里调用的是你修改后的sendto
死循环了啊
IAT hook由于处理dll之类的有点麻烦,而且容易漏钩
所以没有仔细看
但是你前面已经把sendto转到MySendTo了,你这里再调这个不又回到了MySendTo?
核心编程好久没看了
等会有空翻出来看下。
我在我看的核心编程资料中没发现有这样的异常处理代码啊,因为我看的是《Windows核心编程》的电子书,不过你说的很有道理,我也试了在ReplaceIATEntryInOneMod中加上异常处理,改为这样:
void ReplaceIATEntryInOneMod(...)
{
__try
{
...//原函数内容
}
__except(EXCEPTION_ACCESS_VIOLATION) {}
}
运行还是一样的错误啊!!!
{
...
}
__except(EXCEPTION_EXECUTE_HANDLER)
{}