今天看了一篇旧文:Windows 2000下Api函数的拦截分析
---------------------------------------------------------------------
Windows 2000下Api函数的拦截分析
简介: Api拦截并不是一个新的技术,很多商业软件都采用这种技术。对windows的Api函数的拦截,不外乎两种方法,第一种是Mr. Jeffrey Richter 的修改exe文件的模块输入节,种方法,很安全,但很复杂,而且有些exe文件,没有Dll的输入符号的列表,有可能出现拦截不到的情况。第二种方法就是常用的JMP XXX的方法,虽然很古老,却很简单实用。 本文一介绍第二种方法在Win2k下的使用。第二种方法,Win98/me 下因为进入Ring0级的方法很多,有LDT,IDT,Vxd等方法,很容易在内存中动态修改代码,但在Win2k下,这些方法都不能用,写WDM太过复杂,表面上看来很难实现,其实不然。Win2k为我们提供了一个强大的内存Api操作函数---VirtualProtectEx,WriteProcessMemeory,ReadProcessMemeory,有了它们我们就能在内存中动态修改代码了,其原型为: BOOL VirtualProtectEx( // 修改内存页的保护属性
HANDLE hProcess, // 要修改内存的进程句柄
LPVOID lpAddress, // 要修改内存的起始地址
DWORD dwSize, // 修改内存的字节
DWORD flNewProtect, // 修改后的内存属性
PDWORD lpflOldProtect // 修改前的内存属性的地址
);
BOOL WriteProcessMemory(
HANDLE hProcess, // 要写进程的句柄
LPVOID lpBaseAddress, // 写内存的起始地址
LPVOID lpBuffer, // 写入数据的地址
DWORD nSize, // 要写的字节数
LPDWORD lpNumberOfBytesWritten // 实际写入的子节数
);
BOOL ReadProcessMemory(
HANDLE hProcess, // 要读进程的句柄
LPCVOID lpBaseAddress, // 读内存的起始地址
LPVOID lpBuffer, // 读入数据的地址
DWORD nSize, // 要读入的字节数
LPDWORD lpNumberOfBytesRead // 实际读入的子节数
); 具体的参数请参看MSDN帮助。在Win2k下因为Dll和所属进程在同一地址空间,这点又和Win9x/me存在所有进程存在共享的地址空间不同,因此,必须通过钩子函数和远程注入进程的方法,现以一个简单采用钩子函数对MessageBoxA进行拦截例子来说明: 其中Dll文件为: HHOOK g_hHook;
HINSTANCE g_hinstDll;
FARPROC pfMessageBoxA;
int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
BYTE OldMessageBoxACode[5],NewMessageBoxACode[5];
HMODULE hModule ;
DWORD dwIdOld,dwIdNew;
BOOL bHook=false;
void HookOn();
void HookOff();
BOOL init();
LRESULT WINAPI MousHook(int nCode,WPARAM wParam,LPARAM lParam);
// DLL文件的入口点
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if(!init())
{
MessageBoxA(NULL, "Init", "ERROR", MB_OK);
return(false);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
if(bHook) UnintallHook();
break;
}
return TRUE;
}
LRESULT WINAPI Hook(int nCode,WPARAM wParam,LPARAM lParam)//空的钩子函数
{
return(CallNextHookEx(g_hHook,nCode,wParam,lParam));
}
HOOKAPI2_API BOOL InstallHook()//输出安装空的钩子函数
{
g_hinstDll=LoadLibrary("HookApi2.dll");
g_hHook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)Hook,g_hinstDll,0);
if (!g_hHook)
{
MessageBoxA(NULL,"SET ERROR","ERROR",MB_OK);
return(false);
} return(true);
}
HOOKAPI2_API BOOL UninstallHook()//输出御在钩子函数
{
return(UnhookWindowsHookEx(g_hHook));
} BOOL init()//初始化得到MessageBoxA的地址,并生成Jmp XXX(MyMessageBoxA)的跳转指令
{
hModule = LoadLibrary("user32.dll");
pfMessageBoxA = GetProcAddress(hModule, "MessageBoxA");
if(pfMessageBoxA == NULL)
return false;
_asm
{
lea edi,OldMessageBoxACode
mov esi,pfMessageBoxA
cld
movsd
movsb
}
NewMessageBoxACode[0] = 0xe9;//jmp MyMessageBoxA的相对地址的指令
_asm
{
lea eax,MyMessageBoxA
mov ebx,pfMessageBoxA
sub eax,ebx
sub eax,5
mov dword ptr [NewMessageBoxACode+1],eax
}
dwIdNew = GetCurrentProcessId(); //得到所属进程的ID
dwIdOld = dwIdNew;
HookOn();//开始拦截
return(true);
}
int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption, UINT uType )
//首先关闭拦截,然后才能调用被拦截的Api 函数
{
int nReturn=0;
HookOff();
nReturn = MessageBoxA(hWnd, "Hook", lpCaption, uType);
HookOn();
return(nReturn);
}
void HookOn()
{
HANDLE hProc;
dwIdOld=dwIdNew;
hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld);//得到所属进程的句柄
VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwIdOld);
//修改所属进程中MessageBoxA的前5个字节的属性为可写
WriteProcessMemory(hProc,pfMessageBoxA,NewMessageBoxACode,5,0);
//将所属进程中MessageBoxA的前5个字节改为JMP 到MyMessageBoxA
VirtualProtectEx(hProc,pfMessageBoxA,5,dwIdOld,&dwIdOld);
//修改所属进程中MessageBoxA的前5个字节的属性为原来的属性
bHook=true;
}
void HookOff()//将所属进程中JMP MyMessageBoxA的代码改为Jmp MessageBoxA
{
HANDLE hProc;
dwIdOld=dwIdNew;
hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld);
VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwIdOld);
WriteProcessMemory(hProc,pfMessageBoxA,OldMessageBoxACode,5,0);
VirtualProtectEx(hProc,pfMessageBoxA,5,dwIdOld,&dwIdOld);
bHook=false;
}
//测试文件:
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
if(!InstallHook())
{
MessageBoxA(NULL,"Hook Error!","Hook",MB_OK);
return 1;
}
MessageBoxA(NULL,"TEST","TEST",MB_OK);//可以看见Test变成了Hook,也可以在其他进程中看见
if(!UninstallHook())
{
MessageBoxA(NULL,"Uninstall Error!","Hook",MB_OK);
return 1;
}
return 0;
}
---------------------------------------------------------------------对其中的汇编部分:
_asm
{
lea eax,MyMessageBoxA
mov ebx,pfMessageBoxA
sub eax,ebx
sub eax,5
mov dword ptr [NewMessageBoxACode+1],eax
}
的作用不是太明白,为什么要sub eax,ebx,sub eax,5 ??
哪位明白给讲讲~~
---------------------------------------------------------------------
Windows 2000下Api函数的拦截分析
简介: Api拦截并不是一个新的技术,很多商业软件都采用这种技术。对windows的Api函数的拦截,不外乎两种方法,第一种是Mr. Jeffrey Richter 的修改exe文件的模块输入节,种方法,很安全,但很复杂,而且有些exe文件,没有Dll的输入符号的列表,有可能出现拦截不到的情况。第二种方法就是常用的JMP XXX的方法,虽然很古老,却很简单实用。 本文一介绍第二种方法在Win2k下的使用。第二种方法,Win98/me 下因为进入Ring0级的方法很多,有LDT,IDT,Vxd等方法,很容易在内存中动态修改代码,但在Win2k下,这些方法都不能用,写WDM太过复杂,表面上看来很难实现,其实不然。Win2k为我们提供了一个强大的内存Api操作函数---VirtualProtectEx,WriteProcessMemeory,ReadProcessMemeory,有了它们我们就能在内存中动态修改代码了,其原型为: BOOL VirtualProtectEx( // 修改内存页的保护属性
HANDLE hProcess, // 要修改内存的进程句柄
LPVOID lpAddress, // 要修改内存的起始地址
DWORD dwSize, // 修改内存的字节
DWORD flNewProtect, // 修改后的内存属性
PDWORD lpflOldProtect // 修改前的内存属性的地址
);
BOOL WriteProcessMemory(
HANDLE hProcess, // 要写进程的句柄
LPVOID lpBaseAddress, // 写内存的起始地址
LPVOID lpBuffer, // 写入数据的地址
DWORD nSize, // 要写的字节数
LPDWORD lpNumberOfBytesWritten // 实际写入的子节数
);
BOOL ReadProcessMemory(
HANDLE hProcess, // 要读进程的句柄
LPCVOID lpBaseAddress, // 读内存的起始地址
LPVOID lpBuffer, // 读入数据的地址
DWORD nSize, // 要读入的字节数
LPDWORD lpNumberOfBytesRead // 实际读入的子节数
); 具体的参数请参看MSDN帮助。在Win2k下因为Dll和所属进程在同一地址空间,这点又和Win9x/me存在所有进程存在共享的地址空间不同,因此,必须通过钩子函数和远程注入进程的方法,现以一个简单采用钩子函数对MessageBoxA进行拦截例子来说明: 其中Dll文件为: HHOOK g_hHook;
HINSTANCE g_hinstDll;
FARPROC pfMessageBoxA;
int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
BYTE OldMessageBoxACode[5],NewMessageBoxACode[5];
HMODULE hModule ;
DWORD dwIdOld,dwIdNew;
BOOL bHook=false;
void HookOn();
void HookOff();
BOOL init();
LRESULT WINAPI MousHook(int nCode,WPARAM wParam,LPARAM lParam);
// DLL文件的入口点
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if(!init())
{
MessageBoxA(NULL, "Init", "ERROR", MB_OK);
return(false);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
if(bHook) UnintallHook();
break;
}
return TRUE;
}
LRESULT WINAPI Hook(int nCode,WPARAM wParam,LPARAM lParam)//空的钩子函数
{
return(CallNextHookEx(g_hHook,nCode,wParam,lParam));
}
HOOKAPI2_API BOOL InstallHook()//输出安装空的钩子函数
{
g_hinstDll=LoadLibrary("HookApi2.dll");
g_hHook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)Hook,g_hinstDll,0);
if (!g_hHook)
{
MessageBoxA(NULL,"SET ERROR","ERROR",MB_OK);
return(false);
} return(true);
}
HOOKAPI2_API BOOL UninstallHook()//输出御在钩子函数
{
return(UnhookWindowsHookEx(g_hHook));
} BOOL init()//初始化得到MessageBoxA的地址,并生成Jmp XXX(MyMessageBoxA)的跳转指令
{
hModule = LoadLibrary("user32.dll");
pfMessageBoxA = GetProcAddress(hModule, "MessageBoxA");
if(pfMessageBoxA == NULL)
return false;
_asm
{
lea edi,OldMessageBoxACode
mov esi,pfMessageBoxA
cld
movsd
movsb
}
NewMessageBoxACode[0] = 0xe9;//jmp MyMessageBoxA的相对地址的指令
_asm
{
lea eax,MyMessageBoxA
mov ebx,pfMessageBoxA
sub eax,ebx
sub eax,5
mov dword ptr [NewMessageBoxACode+1],eax
}
dwIdNew = GetCurrentProcessId(); //得到所属进程的ID
dwIdOld = dwIdNew;
HookOn();//开始拦截
return(true);
}
int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption, UINT uType )
//首先关闭拦截,然后才能调用被拦截的Api 函数
{
int nReturn=0;
HookOff();
nReturn = MessageBoxA(hWnd, "Hook", lpCaption, uType);
HookOn();
return(nReturn);
}
void HookOn()
{
HANDLE hProc;
dwIdOld=dwIdNew;
hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld);//得到所属进程的句柄
VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwIdOld);
//修改所属进程中MessageBoxA的前5个字节的属性为可写
WriteProcessMemory(hProc,pfMessageBoxA,NewMessageBoxACode,5,0);
//将所属进程中MessageBoxA的前5个字节改为JMP 到MyMessageBoxA
VirtualProtectEx(hProc,pfMessageBoxA,5,dwIdOld,&dwIdOld);
//修改所属进程中MessageBoxA的前5个字节的属性为原来的属性
bHook=true;
}
void HookOff()//将所属进程中JMP MyMessageBoxA的代码改为Jmp MessageBoxA
{
HANDLE hProc;
dwIdOld=dwIdNew;
hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld);
VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwIdOld);
WriteProcessMemory(hProc,pfMessageBoxA,OldMessageBoxACode,5,0);
VirtualProtectEx(hProc,pfMessageBoxA,5,dwIdOld,&dwIdOld);
bHook=false;
}
//测试文件:
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
if(!InstallHook())
{
MessageBoxA(NULL,"Hook Error!","Hook",MB_OK);
return 1;
}
MessageBoxA(NULL,"TEST","TEST",MB_OK);//可以看见Test变成了Hook,也可以在其他进程中看见
if(!UninstallHook())
{
MessageBoxA(NULL,"Uninstall Error!","Hook",MB_OK);
return 1;
}
return 0;
}
---------------------------------------------------------------------对其中的汇编部分:
_asm
{
lea eax,MyMessageBoxA
mov ebx,pfMessageBoxA
sub eax,ebx
sub eax,5
mov dword ptr [NewMessageBoxACode+1],eax
}
的作用不是太明白,为什么要sub eax,ebx,sub eax,5 ??
哪位明白给讲讲~~
mov ebx,pfMessageBoxA =>取pfMessageBoxA(MessageBoxA)函数地址
sub eax,ebx =>计算MessageBoxA和MyMessageBoxA的偏移
sub eax,5 =>再空出5字节(Jmp XXXXXX)指令长度
mov dword ptr [NewMessageBoxACode+1],eax =>NewMessageBoxACode反汇编后就变成=>Jmp MyMessageBoxA
NewMessageBoxACode在HookOn中被用于修改MessageBoxA的头5条指令。
该Hook方法有危险,它假设被Hook函数头五字节是整操作,对大部分API确实这样,但是还有一些并不是这样的会导致被Hook进程崩溃。
楼主你了解一下pe头就知道建议最好不要自己做,你可以参照windows核心编程里的代码,不过在windows2000下有点问题,要修改一下
或者用detous,微软自己的东西,还是蛮可靠的
mov ebx,pfMessageBoxA =>取pfMessageBoxA(MessageBoxA)函数地址
sub eax,ebx =>计算MessageBoxA和MyMessageBoxA的偏移
sub eax,5 =>再空出5字节(Jmp XXXXXX)指令长度
mov dword ptr [NewMessageBoxACode+1],eax =>NewMessageBoxACode反汇编后就变成=>Jmp MyMessageBoxA
----------------
上面翻译成C语言如下:
HMODULE g_hModule = NULL;
PROC m_pfnAdr = NULL;
g_hModule = ::LoadLibrary("user32.dll");
m_pfnAdr = ::GetProcAddress(g_hModule,"MessageBoxA");
BYTE saveCode[8];
BYTE newCode[8]={0xB8,0x00,0x00,0x00,0x00,0xFF,0xE0,0x00};//0xB8是JMP指令
*(DWORD *)(newCode+1)=(DWORD)MyMessageBox;
//上面的那句相当于上面的所有汇编代码,生成相应的跳转(JMP XXX)代码.
::memcpy(m_pfnAdr,newCode,8);//这句话是将新的代码覆盖到MessageBoxA代码的前8个字节.
这样一但调用MessageBox,就会执行newCode,也就是跳转到MyMessageBox..
//上面的处理省略了修改页属性.下面是完整代码:
HMODULE g_hModule = NULL;
PROC m_pfnAdr = NULL;
BYTE saveCode[8];
BYTE newCode[8]={0xB8,0x00,0x00,0x00,0x00,0xFF,0xE0,0x00};int WINAPI MyMessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
BOOL InstallHook();
BOOL UnstallHook();int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
InstallHook();
MessageBox(NULL,"挂钩之前","注意",MB_OK);
return 0;
}int WINAPI MyMessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType)
{
UnstallHook();
char msg[255]="";
::strcpy(msg,lpText);
::strcat(msg,"--NFC");
::MessageBox(hWnd,msg,lpCaption,uType);
return 0;
}BOOL InstallHook()
{
g_hModule = ::LoadLibrary("user32.dll");
if (g_hModule==NULL)
{
return FALSE;
}
m_pfnAdr = ::GetProcAddress(g_hModule,"MessageBoxA");
if (m_pfnAdr==NULL)
{
return FALSE;
}
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
::VirtualQuery(m_pfnAdr,&mbi,sizeof(mbi));
::VirtualProtect(m_pfnAdr,8,PAGE_READWRITE,&dwOldProtect);
::memcpy(saveCode,m_pfnAdr,8); *(DWORD *)(newCode+1)=(DWORD)MyMessageBox; ::memcpy(m_pfnAdr,newCode,8); ::VirtualProtect(m_pfnAdr,8,mbi.Protect,0);
return TRUE;
}BOOL UnstallHook()
{
if (m_pfnAdr!=NULL)
{
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
::VirtualQuery(m_pfnAdr,&mbi,sizeof(mbi));
::VirtualProtect(m_pfnAdr,8,PAGE_READWRITE,&dwOldProtect);
::memcpy(m_pfnAdr,saveCode,8);
::VirtualProtect(m_pfnAdr,8,mbi.Protect,0);
}
return TRUE;
}
mov ebx,pfMessageBoxA =>取pfMessageBoxA(MessageBoxA)函数地址
sub eax,ebx =>计算MessageBoxA和MyMessageBoxA的偏移
sub eax,5 =>再空出5字节(Jmp XXXXXX)指令长度
mov dword ptr [NewMessageBoxACode+1],eax =>NewMessageBoxACode反汇编后就变成=>Jmp MyMessageBoxA
------------------------------------------------------
上面计算出来的eax是MyMessageBoxA相对于MessageBoxA的偏移,“jmp 偏移地址”这样可以跳过去么?我觉得应该是个“绝对”地址才能跳~~
在完成自己的代码之后,把原来的指令恢复了就可以了。
不一定需要5字节,也不一定要用JMP,大的流程是这样,小细节上处理一下还是很不错的。
楼主可以去看下偶blog上的一篇文章,关于Hook API的,相信对你会有帮助的。