csdn的博客不支持ie6。前两天电脑崩溃,重装后用ie6无法更新博客了。现在装上ie8就可以了。
继续nhw32.dll的分析。
nhw32.dll没有使用hook API,而是采用了一种叫做API拦截的技术,就是把需要拦截的函数的入口部分的7个字节改为一条跳转语句,跳转到我们自己定义的函数入口,然后在我们自己定义的函数里面恢复这7个字节的内容。接着,就执行我们自己函数的指令,执行完成后,跳转到拦截的API入口(因为开始的7个字节已经恢复,所以可以正常执行API)。执行完API后,再重新修改API入口的7个字节,准备下一次拦截。源码的流程如下
DLLEXPORT BOOL WINAPI NHTextOutA(......)
{
// restore
RestoreWin32Api(&g_TextOutAHook, HOOK_NEED_CHECK);
// Our instruments
......
// call TextOutA
TextOutA(hdc, nXStart, nYStart, lpString, cbString);
// hook again
HookWin32Api(&g_TextOutAHook, HOOK_NEED_CHECK);
return TRUE;
}
RestoreWin32Api就是恢复TextOutA的函数入口的7个字节。
HookWin32Api就是重新修改API入口的7个字节。
下面的代码显示了这7个字节的内容,第1个字节是jmp的机器码,第2到第5个字节是段内偏移量,第6,7个字节是段选择符。
void MakeJMPCode(LPBYTE lpJMPCode, LPVOID lpCodePoint)
{
BYTE temp;
WORD wHiWord = HIWORD(lpCodePoint);
WORD wLoWord = LOWORD(lpCodePoint);
WORD wCS;
_asm // 取当前的代码段选择符(cs)
{
push ax;
push cs;
pop ax;
mov wCS, ax;
pop ax;
};
lpJMPCode[0] = 0xea; // 填入jmp指令的机器码
temp = LOBYTE(wLoWord); // -------------------------
lpJMPCode[1] = temp;
temp = HIBYTE(wLoWord);
lpJMPCode[2] = temp; // 填入地址,在内存中的顺序为
temp = LOBYTE(wHiWord); // Point: 0x1234
lpJMPCode[3] = temp; // 内存中:4321
temp = HIBYTE(wHiWord);
lpJMPCode[4] = temp; // -------------------------
temp = LOBYTE(wCS); // 填入段选择符
lpJMPCode[5] = temp;
temp = HIBYTE(wCS);
lpJMPCode[6] = temp;
return;
}
PS:曾经疑惑的两个问题
1 因为钩子函数是在dll中,如果不同的进程把nhw32.dll加载到不同的内存地址空间中。就会产生不同的函数入口地址,那在修改API入口的时候应该用哪一个呢?我目前的结论是,nhw32.dll正常情况下应该只被加载一次,如:金山词霸,其他的应用程序不会加载nhw32.dll。但是,如果有多个进程加载nhw32.dll,应该是以最后一次执行HookWin32Api为准。
2 应用程序如何跳转到取词程序的进程地址空间中?代码已经很清晰了,通过在jmp指令中指定cs就可以执行任意进程空间中的函数了。
现在我还不太清楚的问题是:如果多个dll拦截相同的API,处理方法又不相同,在进程的环境中安全吗?如果不安全。那么,该如何保证在多进程环境中的安全性呢?