确实是班门弄斧,因为我是学VB的,虽然VB6和.NET都算入门吧,可真正拿VC++写程序还是第一次。学B/C/D的就莫看了。
由于是用上两篇写的远线程调用,所以很多参数等等都是传入的,而不是直接获取的。
下面就一个用VB的人能理解为目标做简单说明和注释:
1、VC里面声明变量和VB不一样:类型在前,名称在后,例如:
BYTE B ;
等同于:
dim B as byte
VC用分号来识别行结束,而VB用回车;VB用圆括号表示数组,VC用方括号……不多说了,我也不是很清楚,总之习惯就好
2、VC里面的函数、变量等等区分大小写,VB不区分,这个一定要注意!
3、类型转换:VB有显式或隐式的,而VC必须用显式的——即时内存里面存的就是那个东东你也不能直接拿来用,够严格,不过指针够灵活也够太灵活……转换时使用的方法就是在圆括号里面写入你要的类型,后面跟着你的参数就可以。例如:
(DWORD)LEN;就是把LEN转换成DWORD类型,据我理解要么是UINTEGER,要么是INTEGER,可能不同API要求不同,我一直都是按INTEGER处理的,也就是说,前面那句相当于CTYPE(LEN,INTEGER)了。
4、&将会吧后面的变量变为指针,*将会把指针变成相应的数据……说的不确切,应该是转换吧
5、其他看看就知道了,VC用API、常数等根本不声明……而是导入.H文件吧(头文件,而且里面可以用宏来改变API书写的名称,就像VB声明API时那个别名吧),反正这个工程里没用到。
以上言论如有误导,请尽快指出………………以免造孽更深…………谢谢………………
//自定义APIHOOK结构
typedef struct
{
FARPROC funcaddr; //RECV函数所在地址
BYTE olddata[8]; //RECV函数开头的8个字节原始数据
BYTE newdata[8]; //我们要写入的数据,汇编代码在下面,利用了EAX存储要跳转的地址,并且FF E0这个操作是绝对地址的
}HOOKSTRUCT;
//0-4 : move eax 0x00000000 //绝对地址,避免了一个换算,其实这个换算也简单,ADDRESS-MFUNADDRESS-5即是
//5-7 : jmp eax
HWND _fHandle ; //接收消息窗口句柄
DWORD _dwIdNew ; //PID
HANDLE hProc; //进程内核句柄,就是OPENPROCESS打开PID得到的那个东东——读写内存用到
////////////////////////////////////////
DWORD _ws2_RecvIndex = 1 ; //ws2_32.dll RECVHOOK标志,实际和HOOK没关系,但是当你HOOK的函数多起来,你得区分是哪个函数发来的消息啊!
HOOKSTRUCT _ws2_RecvHook; //ws2_32.dll HOOK结构
////////////////////////////////////////
//这下面的这些家伙,就是一个定义,和下面写的函数实体一样声明即可,VB里不用这么干编译器也能识别都有哪些函数,可VC不成
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);
//下面传入了一些参数:被注入进程PID,要接收返回信息窗口的句柄
extern "C" _declspec(dllexport) void __stdcall Init(DWORD PID ,HWND FormHandle)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); //这个……VC里叫宏吧,哎……说来话长,这句去掉也无所谓的
_dwIdNew = PID ; //备份PID,其实后面代码没用到,主要是为了识别注入了多个程序的话,是哪个程序发回来的信息呢
_fHandle = FormHandle ; //接收消息的窗口句柄,SendMsg函数用到
hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, _dwIdNew); //打开进程,进一步操作他
}
//下面这个用来:记录被HOOK函数前8字节原始数据、构建新的8字节代码、将新代码替换RECV函数原来代码的
//说明全部都是以RECV函数为例,其实这个函数是一个通用函数
bool HOOKAPI(DWORD Address,HOOKSTRUCT *hookfunc,DWORD mFncAddress)
{
hookfunc->funcaddr = (FARPROC)Address; //这个地方用了一个转换,把传入值转换成何结构当中声明的一样
memcpy(hookfunc->olddata, hookfunc->funcaddr, 8); //记录RECV函数前8字节原始数据
//下面开始构建汇编代码{0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00 }
hookfunc->newdata[0]=0xB8; //MOVE EAX (EAX后面的数就是下面4字节了)
hookfunc->newdata[1]=0x00;
hookfunc->newdata[2]=0x00;
hookfunc->newdata[3]=0x00;
hookfunc->newdata[4]=0x00; //到这里是 MOVE EAX 00000000 ,这里的地址,将会在下面被替换(注意是绝对地址)
hookfunc->newdata[5]=0xFF;
hookfunc->newdata[6]=0xE0;//到这里是 JMP EAX
hookfunc->newdata[7]=0xCC;//填充一个INT3 ,其实NOP(&h90)啥的也行,换句话说这个不改、不理他都可以,不过翻看帖子时看到一些人说CPU识别错代码,谁知道真假,还是放个显眼的比较保险……
memcpy(&hookfunc->newdata[1], &mFncAddress, 4); //这里替换了上面提到的 00000000 为我们用来代替RECV的函数的地址
HookOnOne(hookfunc); //将RECV函数前8字节替换为我们构建的代码——开始HOOK RECV
return true;
}
////////////////////////////////////////////////////////////////////////////////////
//recv,这个函数将实现RECV函数的替换:只需执行一次即可
////////////////////////////////////////////////////////////////////////////////////
extern "C" _declspec(dllexport) bool __stdcall Hook_ws2_Recv(DWORD RecvAddress)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HOOKAPI(RecvAddress,&_ws2_RecvHook,(DWORD)m_ws2_recv); //这里,(DWORD)m_ws2_recv的返回值就是m_ws2_recv函数的地址!你看人家VC多方便……
return true;
}
//显然了,这个事解除RECV HOOK的
extern "C" _declspec(dllexport) bool __stdcall UnHook_ws2_Recv()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HookOffOne(&_ws2_RecvHook);
return true;
}
//---------------------------------------------------------------------------
//这个函数,就是我们用来替换RECV函数的家伙了,必须让这个家伙和RECV函数声明相同,这么相同呢,很简单,VC++.NET也有类似VB的代码提示(虽然少点),找一个适当的地方打上recv(小写)然后打圆括号……看到了?
int WINAPI m_ws2_recv(SOCKET s, char FAR *buf, int len, int flags)
{
int nReturn = 0; //这个家伙记录了RECV的返回值,-1为错误,我们不处理,0为断开,其他嘛,就是实际从SOCKET缓冲区复制了多少字节到RECV缓冲区。简单的说,-1,0都不处理,其他值就是我们要从BUF里面读多少字节了!
HookOffOne(&_ws2_RecvHook); //先关闭HOOK,因为已经进入我们的函数了
nReturn = recv(s, buf, len, flags); //先运行原来的RECV,否则我们不能得到或不能得到全部被复制的内容
HookOnOne(&_ws2_RecvHook); //继续HOOK
//sndmsg((DWORD)len,(DWORD)nReturn);
if ( nReturn == SOCKET_ERROR )
return -1 ;
SendMsg(buf ,nReturn,_ws2_RecvIndex); //发送BUF里面NRETURN个字节到接收窗体
return(nReturn);
}
/////////////////////////////////////////////////////////////////////////////
//WSARECV
/////////////////////////////////////////////////////////////////////////////////
//将原来的函数内容写回——关闭HOOK
void HookOffOne(HOOKSTRUCT *hookfunc)
{
WriteProcessMemory(hProc, hookfunc->funcaddr, hookfunc->olddata, 8, 0);
}
//---------------------------------------------------------------------------
//将我们构建的代码写入——打开HOOK
void HookOnOne(HOOKSTRUCT *hookfunc)
{
WriteProcessMemory(hProc, hookfunc->funcaddr, hookfunc->newdata, 8, 0);
}
//发送WM_COPYDATA消息到指定窗口
void SendMsg(char *buf,DWORD len,DWORD Index)
{
COPYDATASTRUCT cds;
cds.dwData = 0;//sizeof(COPYDATASTRUCT); //有的说这个参数影响发送,还没用到,本来是存PID的,个人觉得应该不会影响
cds.cbData = len; //LPDATA里面的字节数
cds.lpData = buf; //实际数据
SendMessage(_fHandle,WM_COPYDATA,(WPARAM)Index,(LPARAM)&cds); //发到INIT里面指定的那个窗口
}
今天来编辑一下帖子,因为用VB.NET完成了HOOK工作。
其实用VB.NET HOOK本进程API早就弄好了,并且用了一段时间,感觉还是满意的。但是注入其他进程一直在用VC,写起来代码真的不如VB.NET顺手,于是想到注入VB.NET的DLL到其他进程,其实这件事很早就有人在做,主要是给对方进程加载CLR的问题,其实这项工作都是由mscoree.dll完成的,本想用VB.NET写一段汇编代码注入到对方进程然后远线程,可看了看工作量确实不小,于是还是使用了一个VC写的DLL,导出了一个函数,然后远线程调用它用以加载CLR和VB.NET的DLL,这个VC的DLL很简单,只是调用CorBindToRuntimeEx函数来加载CLR,并由之调用VB.NET写的DLL(就是一个正常的类库,无需任何处理)中的方法。这样既然可以调用VB.NET的DLL中的方法了,那么接下来只需要在VB.NET的DLL中进行处理就行了。总结起来,主要是3块:
1、VC的DLL:导出一个函数,用来加载CLR而后加载VB.NET的DLL并调用其指定方法,十来行代码而已。
2、注入器:远线程运行API的能力(这个代码很久之前也发过了),当然,根据需要还可能需要设置一些进程访问权限或者更底层的东西。
3、VB.NET的DLL:公开一个方法,使得CLR能够找到并直接调用它。接下来的事情都在这个DLL里面处理就行了,爱启动线程启动线程,想读啥就读啥,想写啥就写啥吧。结合以前完成的HOOK本进程API的一些方法,完全可以用托管代码来HOOK和处理了。
具体实现就不贴了,毕竟不该归为原创,网上已经有很多类似代码了,只是直接调用CorBindToRuntimeEx,使用远线程的并不是很多,之所以使用远线程是因为可以在VC的DLL的导出函数那里传入一些参数。例如:加载的CLR版本,标志,要调用的托管DLL,类,方法,参数等都可以由注入器传递,从而提高了通用性和控制的灵活性。毕竟咱还没到纠结到底几个DLL的境界,当CLR加载完成并运行托管DLL时对方进程还不知道多出来多少DLL呢。