1. 指令指针寄存器与正在执行指令指针关系:
I: CPU正在执行指令;
xIP: CPU指令指针寄存器内容;
xIP(I): CPU正在执行的指令I的指针;
LEN(I): 指令I的长度。
1)顺序执行:xIP = xIP(I) + LEN(I);
2)相对跳转:xIP = xIP(I) + LEN(I) + REL_DISPLACEMENT_CONST;
3)绝对跳转:xIP = Target_CONST;
2. ATL:: _stdcallthunk 代码片断(节选自atlstdthunk.h)
struct _stdcallthunk
{
DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
DWORD m_this; //
BYTE m_jmp; // jmp WndProc
DWORD m_relproc; // relative jmp
BOOL Init(DWORD_PTR proc, void* pThis)
{
m_mov = 0x042444C7; //C7 44 24 0C
m_this = PtrToUlong(pThis);
m_jmp = 0xe9;
m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
// write block from data cache and
// flush from instruction cache
FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk));
return TRUE;
}
//some thunks will dynamically allocate the memory for the code
void* GetCodeAddress()
{
return this;
}
// code ignored ……
};
3. 数据作为代码运行
实际使用时,_stdcallthunk结构作为代码使用。经过Init()后,被解释为以下两条指令(X86机器):
Mov dword ptr[esp+4], pThis_CONST;
Jmp REL_DISPLACEMENT_CONST;
其中 pThis_CONST是CWindowXXX的指针;
REL_DISPLACEMENT_CONST是跳转到实际WndProc的相对偏移量。那为什么 REL_DISPLACEMENT_CONST是 “_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk))); ”呢?稍候解释。
下面是一个可能的伪码例子:
WNDPROC pWndProc = (WNDPROC)( _stdcallthunkOBJ. GetCodeAddress() );
SetWindowLongPtr( hwnd, GWLP_WNDPROC, pWndProc);
4. 两条指令的解释
当WndProc被调用时,程序堆栈如下:(地址约定:高地址在上,低地址在下)
LPARAM
WPARAM
UINT
HWND ß esp+4
RetAddr( from WNdProc) ß esp
所以当第一条指令Mov dword ptr[esp+4], pThis_CONST 执行后,程序堆栈如下:
LPARAM
WPARAM
UINT
pThis_CONST ß esp+4
RetAddr( from WNdProc) ß esp
当第二条指令Jmp REL_DISPLACEMENT_CONST 执行时,xIP和xIP(I)的关系如下(重复地址约定:高地址在上,低地址在下):
Others …. ß_stdcallthunk结构的基址+_stdcallthunk结构大小
Jmp ….. (第二条指令) ß xIP(I)
Mov ….. (第一条指令) ß_stdcallthunk结构的基址
由于xIP = xIP(I) + LEN(I) + REL_DISPLACEMENT_CONST;
显然 xIP(I) + LEN(I) = _stdcallthunk结构的基址+_stdcallthunk结构大小;
又因为 REL_DISPLACEMENT_CONST = _relproc = DWORD((INT_PTR)proc - (_stdcallthunk结构的基址+_stdcallthunk结构大小);
所以 xIP = xIP(I) + LEN(I) + REL_DISPLACEMENT_CONST
= (_stdcallthunk结构的基址+_stdcallthunk结构大小)+ (DWORD((INT_PTR)proc - (_stdcallthunk结构的基址+_stdcallthunk结构大小))
= proc;
既下一条将要执行的指令是实际WndProc的首条指令。
如果使用绝对寻址,要好理解的多,可能是基于性能考虑,使用相对寻址。X64使用寄存器绝对寻址(rcx rax),好理解一些,但显然须符合调用约定。
5.总结
1) 数据被解释为代码;
2) 相对跳转正好跳到实际WndProc。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/benny5609/archive/2008/06/30/2597419.aspx