【HOOK RECV的困惑】

清晨曦月
博客专家认证
2009-04-17 10:04:16
背景是这样的……
写了一DLL,远线程注入之后,发现HOOK RECV安装和运行都非常良好,就是封包丢了无数………………哈哈

自定义RECV是这样的:

int WINAPI m_ws2_recv(SOCKET s, char FAR *buf, int len, int flags)
{
int nReturn = 0;

HookOffOne(&_ws2_RecvHook); //恢复原RECV函数的入口
nReturn = recv(s, buf, len, flags); //执行原RECV函数
HookOnOne(&_ws2_RecvHook); //继续截获

if ( nReturn == -1 ) //发生错误时返回
return -1 ;

char *tmpbuf = new char[nReturn]; //定义一个临时缓冲区,存储BUF内容
//memset(tmpbuf,0,nReturn);
memcpy(tmpbuf,buf,nReturn); //将BUF复制到临时缓冲区
SendMsg(tmpbuf ,nReturn,_ws2_RecvIndex); //发送WM_COPYDATA到接收窗体

delete tmpbuf; //删除临时缓冲区
return(nReturn);
}

个人觉得没问题(临时缓冲区那段可以删除直接发送BUF,懒,没改),可封包丢失严重。。。。。。
看了一些代码,在复制缓冲区BUF时用的都是LEN为长度,可据我了解,这个数据是不可靠的,RECV函数返回值才是RECV从SOCKET那里真正复制的内容长度,可我这样写丢包啊!

还请达人指教!!最好有代码,哈哈哈,虽然要代码很无耻,但我VC++不上手,VB才是本行……

对了,还有个小事,VC++.NET非常不会,导致导出函数那个@和后面的堆栈大小还有前面的_去不掉呢,我这么声明咋不行?
extern "C" __declspec(dllexport) void __stdcall Init(DWORD PID ,HWND FormHandle)
{
//函数体
}
...全文
841 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
迷途的书童 2009-04-19
  • 打赏
  • 举报
回复
有个小事,VC++.NET非常不会,导致导出函数那个@和后面的堆栈大小还有前面的_去不掉呢。

这个是没有把包含改函数的源文件加到工程中去!

感觉应该把把临时缓冲区隔初始删除,在发送前!
清晨曦月 2009-04-18
  • 打赏
  • 举报
回复
加分去!呵呵,在这个板块就能开100的…………不行再开帖~~~~真和这个问题飚上了!
清晨曦月 2009-04-18
  • 打赏
  • 举报
回复
嗷嗷,还是老大你敏锐!!
我真败给这个写程序的败家子了……

今天下午,用一个Sniff的程序(繁体呢,哈哈)来参考一下是不是代码有问题,于是发现,人家HOOK了所有函数,而且……都是ASM写的,哎,无奈功底不好没看大明白,可发现人家得到了全部封包,并且在走棋的时候返回的是WSRV……才恍然大悟:原来我被实实在在的骗了一次!!

于是花了一个来小时,写了一段WSARECV的HOOK,和RECV的HOOK差别还不小,非常详细翻看了WSARECV的说明才搞出来。。还得请教一下,有没有什么简单的方法可以把这个lpNumberOfBytesRecvd指针直接转化成DWORD啊,我用的是
DWORD LEN ;
memcpy(&LEN,lpNumberOfBytesRecvd,4)
VC++.NET实在是不上手。。。但是感觉应该有更简单的方法。


最终的结论应该是:(VISTA下用没用WSARECV还没测试,用来干什么了也不知道,但是只要解了XP这个包,就可以完成记录棋谱的任务了)

在VISTA下就是用的RECV来接收走棋信息,而且封包接近明文了……
在XP下,RECV里面有数据,但是走棋信息是用WSARECV接收,封包也加密了,还没看,可以肯定还是20字节的。。



最后,老大能否指点一下,使用IAT HOOK能否完成封包HOOK呢?如果能,我就花时间去写代码了,哈,如果不能还是省下时间看看孩子…………



今天回顾了一下以前的一个HOOK封包的帖子,在VB区里,哈,里面也有你的回复啊……

另外一个帖子里只有一个顶的。。。分全给了有点不对……麻烦老大去回一下。。哈哈,不在那里挂着了,一起结掉!!

清晨曦月 2009-04-18
  • 打赏
  • 举报
回复
又测试了一下,发现即使在
if ( nReturn == -1 ) //发生错误时返回
return -1 ;
前面调用sndmsg返回NRETURN和LEN等,也有丢失的情况………………真搞不懂了

把代码贴出来


//自定义APIHOOK结构
typedef struct
{
FARPROC funcaddr;
BYTE olddata[8];
BYTE newdata[8];
}HOOKSTRUCT;
//0-4 : move eax 0x00000000 //绝对地址,写了另外一个DLL,用的是相对地址那种,就是直接JMP到我的函数,测试结果和这个一样。。。。都在VISTA下正常,XP下不正常……
//5-7 : jmp eax

HWND _fHandle ; //接收消息窗口句柄
DWORD _dwIdNew ; //PID
HANDLE hProc;
////////////////////////////////////////
DWORD _ws2_RecvIndex; //ws2_32.dll RECVHOOK标志
HOOKSTRUCT _ws2_RecvHook; //ws2_32.dll HOOK结构
////////////////////////////////////////

int WINAPI m_ws2_recv(SOCKET s, char FAR *buf, int len, int flags);
void SendMsg(char *buf,DWORD len,DWORD Index);
void HookOnOne(HOOKSTRUCT *hookfunc);
void HookOffOne(HOOKSTRUCT *hookfunc);
bool HOOKAPI(DWORD Address,HOOKSTRUCT *hookfunc,DWORD mFncAddress);

void sndmsg(DWORD wp,DWORD lp);
BOOL EnableDebugPriv();

extern "C" __declspec(dllexport) void __stdcall Init(DWORD PID ,HWND FormHandle)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
_dwIdNew = PID ;
_fHandle = FormHandle ;
_ws2_RecvIndex = 0 ;
EnableDebugPriv(); //赋予DEBUG权限,因为测试时发现下面的OPENPROCESS不成功,GETLASTERR返回5
hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, _dwIdNew);
}

bool HOOKAPI(DWORD Address,HOOKSTRUCT *hookfunc,DWORD mFncAddress)
{
hookfunc->funcaddr = (FARPROC)Address;
memcpy(hookfunc->olddata, hookfunc->funcaddr, 8);
//{0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00 }
hookfunc->newdata[0]=0xB8;
hookfunc->newdata[1]=0x00;
hookfunc->newdata[2]=0x00;
hookfunc->newdata[3]=0x00;
hookfunc->newdata[4]=0x00;
hookfunc->newdata[5]=0xFF;
hookfunc->newdata[6]=0xE0;
hookfunc->newdata[7]=0xCC;
memcpy(&hookfunc->newdata[1], &mFncAddress, 4);
HookOnOne(hookfunc);
return true;
}

extern "C" __declspec(dllexport) bool __stdcall Hook_ws2_Recv(DWORD RecvAddress)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HOOKAPI(RecvAddress,&_ws2_RecvHook,(DWORD)m_ws2_recv);
return true;
}


extern "C" __declspec(dllexport) bool __stdcall UnHook_ws2_Recv()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HookOffOne(&_ws2_RecvHook);
CloseHandle(hProc);
return true;
}
//---------------------------------------------------------------------------
int WINAPI m_ws2_recv(SOCKET s, char FAR *buf, int len, int flags)
{
int nReturn = 0;

HookOffOne(&_ws2_RecvHook);
nReturn = recv(s, buf, len, flags);
HookOnOne(&_ws2_RecvHook);

sndmsg((DWORD)len,(DWORD)nReturn); //在这里发送一个自定义消息0X40A,在接收方也处理这个消息看看

if ( nReturn == -1 )
return -1 ;

SendMsg(buf ,nReturn,_ws2_RecvIndex); //这个消息是WM_COPYDATA,用来发送封包

return(nReturn);
}
//修改内存读写权限的代码放到接收窗体里去了,直接改成PAGE_EXECUTE_READWRITECOPY了。。。所以这里就没有
void HookOffOne(HOOKSTRUCT *hookfunc)
{
WriteProcessMemory(hProc, hookfunc->funcaddr, hookfunc->olddata, 8, 0);
}
//---------------------------------------------------------------------------
void HookOnOne(HOOKSTRUCT *hookfunc)
{
WriteProcessMemory(hProc, hookfunc->funcaddr, hookfunc->newdata, 8, 0);
}

//用于发送调试用消息
void sndmsg(DWORD wp,DWORD lp)
{
SendMessage(_fHandle,0x401,(WPARAM)wp,(LPARAM)lp);
}

void SendMsg(char *buf,DWORD len,DWORD Index)
{
COPYDATASTRUCT cds;
cds.dwData = 0;//sizeof(COPYDATASTRUCT);
cds.cbData = len;
cds.lpData = buf;
SendMessage(_fHandle,WM_COPYDATA,(WPARAM)Index,(LPARAM)&cds);
}

//抄来的,呵呵,懒啊,所以都没改
BOOL EnableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;

if ( ! OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
return false;

if ( ! LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) )
{
CloseHandle( hToken );
return false;
}

tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if ( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) )
CloseHandle( hToken );

return true;
}
清晨曦月 2009-04-18
  • 打赏
  • 举报
回复
cnzdgs 老大:

恩,HOOK函数能够执行……并且对方还能接收到数据,因为他走棋都是正常的,我写了另外一个函数,用自定义消息发送俩个参数WPARAM,LPARAM,
//用于发送调试用消息
void sndmsg(DWORD wp,DWORD lp)
{
SendMessage(_fHandle,0x401,(WPARAM)wp,(LPARAM)lp);
}
在下面这行之前,调用他,返回NRETURN和LEN(对FLAGS也测试过)

if ( nReturn == -1 ) //发生错误时返回
return -1 ;

可以发现,NRETURN返回0XFFFFFFFF(-1)的时候,对方还是接收到数据了,可我在那里直接返回了。。。并且有时候,他的FLAGS=2,我把他都改成0,nReturn = recv(s, buf, len, 0); //执行原RECV函数
也无济于事……


KeSummer 老大:

自己维护堆栈…………从来没干过。。那看起来非常麻烦了。。。

2、把stdcall去掉试试。。。那样会让参数压栈顺序变化的。。。。不过还是试试,反正是远线程调用的,参数反过来写就可以了……(用VB的多是倒过来的,现在倒回去,变成正向……)

3:对于多线程与指令碎片问题,可以有以下解决方法:
A:使用DETOURS
//这个,努力一下应该能看明白。。。并写出代码
B:使用反汇编引擎,自己拷贝原来的前N条指令生成新的调用函数,在新调用函数末尾JMP回原来的代码
//这个,能否详细解释一下啊,没看懂…………
C:使用IAT HOOK
//这个应该能实现了,因为我的VB.NET写的接收程序里面就是用分析PE的方法得到RECV地址的,而DLL里面的所有导出函数都是由我的主程序远线程调用的;可是也有疑惑。所以当时没这么写。我的认识就是分析PE结构获取一个RECV函数入口,这个入口在IAT这个表里面,修改这个指向RECV的地址为我的函数地址,当我的函数完成再JMP到原来RECV入口上。可看到一些关于RECV的讨论,有的说RECV调用前,将检查IAT表并恢复被修改的指针为正确的RECV地址(不知真假),有的给出好长代码,一大串分析,还说了N多困难没详细看,就被吓到了…………还请指点一二…………



4:可以不使用钩子来实现拦截,方法如下,写一个dll,里面导出recv,其他函数用DLL转发到system32目录下的ws2_32.dll,然后你写的DLL命名为ws2_32.dll放到那个下棋程序的目录下,它就会调用你的RECV了。。这个叫DLL劫持。
这个方法见过……并且对这个想法很佩服……但是好像要写好多好多好多代码,而且,不同系统下WS2_32.DLL的导出函数个数不一样,例如VISTA下的就比XP下的多好几个,貌似通用性不强,编码量也太大了。。。

而且……我觉得我遇到的就是一个封包复制过程中出现的问题,原因不是很明确……并不是HOOK 原理或代码上的严重错误。。。
KeSummer 2009-04-18
  • 打赏
  • 举报
回复
1:获取WS2_32.DLL的路径,然后复制一份,让对方进程加载,在原来RECV入口那里JMP到我的函数,在我的函数结尾JMP到后来加载的 WS2_32.DLL的RECV地址上去。。。没测试不知道能行不…………翻看过一些讨论,很多提到用临界段什么的。。。不过我这水平肯定是写不出来,呵呵,因为VB.NET里面都封装好了,只是注意几个问题就可以了。。哎,从没深入研究过。。。

你会破坏掉堆栈的,除非你的HOOK 函数声明为nake并且自己维护堆栈。

2:
extern "C" __declspec(dllexport) void __stdcall Init(DWORD PID ,HWND FormHandle)
{
//函数体
}

把stdcall去掉试试

3:对于多线程与指令碎片问题,可以有以下解决方法:
A:使用DETOURS
B:使用反汇编引擎,自己拷贝原来的前N条指令生成新的调用函数,在新调用函数末尾JMP回原来的代码
C:使用IAT HOOK


4:可以不使用钩子来实现拦截,方法如下,写一个dll,里面导出recv,其他函数用DLL转发到system32目录下的ws2_32.dll,然后你写的DLL命名为ws2_32.dll放到那个下棋程序的目录下,它就会调用你的RECV了。。这个叫DLL劫持。

cnzdgs 2009-04-18
  • 打赏
  • 举报
回复
这个程序是不是还用其它方法接收了数据?
不要改flag。

另外你已经发了两帖了,不用再发了。
cnzdgs 2009-04-17
  • 打赏
  • 举报
回复
你的意思是Hook函数根本没有执行,还是Hook导致目标程序也收不到数据了?

多线程的情况,你在网上搜索一下detours。
清晨曦月 2009-04-17
  • 打赏
  • 举报
回复
恩,cnzdgs老大提到的多线程的,倒是有个想法,不知道能不能避免,就是改一次,不反复改。

获取WS2_32.DLL的路径,然后复制一份,让对方进程加载,在原来RECV入口那里JMP到我的函数,在我的函数结尾JMP到后来加载的WS2_32.DLL的RECV地址上去。。。没测试不知道能行不…………翻看过一些讨论,很多提到用临界段什么的。。。不过我这水平肯定是写不出来,呵呵,因为VB.NET里面都封装好了,只是注意几个问题就可以了。。哎,从没深入研究过。。。
清晨曦月 2009-04-17
  • 打赏
  • 举报
回复
恩,可以确定是单线程的,丢包……具体就是……我详细说明一下。。为了写一个象棋游戏的棋谱记录,因为那个游戏的棋谱记录文件不是规范的:既不是我们所熟知的炮二平五这样的传统记录方式,也不是UCCI等规范的。所以在研究棋局时非常不方便。

这个源码,在VISTA下编写的,包括接收程序的代码(VB写的),当时测试没有任何问题,可以得到全部封包,因为封包分析我已经做完了,当走棋的时候,会接收到一个20字节的封包,里面的格式是按坐标来表示棋子的移动的。

就这个包来说,就是我必须也是唯一需要的,当游戏显示走棋之前这个封包到达的,被我截获。

可是到XP上测试时,根本不会接收到20字节长的封包,在游戏显示走棋的时候,也根本没有接收到任何封包。

所以判断是丢啦…………
cnzdgs 2009-04-17
  • 打赏
  • 举报
回复
你说的丢包具体是指什么?根据返回值来判断接收的数据长度是正确的。
不过你这样的Hook方法(每次恢复又重新Hook)只能处理单线程,如果进程有多个线程同时调用recv,则只能Hook到其中一个。

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧