最开始实现时是用SetWindowLong中的GWL_USERDATA来传递Button对象的this指针,结果被老板狠批了一顿,在他的点拨下改用自定位代码,还好上次做Hook时就已经弄熟了这个东西,还是比较顺利搞了出来,经过一次修改得到下面的代码
... {
m_bmps = new ButtonMsgProcStruct;
m_bmps->NotifyPointer = NotifyObserver;
m_bmps->ThisPointer = (int)this;
char *start, *end;
_asm
...{
mov start, offset _StartCode;
mov end, offset _EndCode;
}
int code_size = end - start;
memcpy(m_bmps, start, code_size);
return;
_StartCode:
_asm
...{
call lable
lable: pop eax
sub eax,5
mov ecx,dword ptr [eax+20] //this
mov eax,[eax+24]; //NotifyObserver
jmp eax;
}
_EndCode:
return;
}
其中m_bmps变量的定义如下:
struct ButtonMsgProcStruct
... {
char Instruct[20];
int ThisPointer;
NOTIFYPOINTER NotifyPointer;
} ;
Button在创建时调用CreateWindow函数 然后调用SetWindowLong修改Button默认的窗口处理函数
这个时候就是把这块自定位代码作为ButtonMsgProc传入其中 供Windows系统回调
{
m_hwndButton = CreateWindow(m_lpClassName, m_lpWindowName, m_dwStyle, m_x, m_y, m_nWidth, m_nHeight, m_hWndParent, m_hMenu, m_hInstance, m_lpParam);
CreateButtonMsgProcCode(); // 该方法调用后 将创建出自定位代码 以传给下面的函数
m_OldMsgProc = (WNDPROC)SetWindowLong(m_hwndButton, GWL_WNDPROC, (LONG)m_bmps);
}
当Button上产生消息时 系统首先就回调我们的消息处理函数 在这里就是我们这片自定位代码
在代码尾部 存放了对象的This指针和NotifyObserver函数的入口地址 所以我们把This放入ECX
然后再掉用NotifyObserver方法 为什么我们没有Call呢? 因为Windows回调的时候就将我们需要的参数压栈了
为了保持堆栈不发生变化 所以调用了JMP指令 如果我们使用call的话 就会压入返回地址
这样就不能直接使用Windows为我们压栈的参数了 而不得不自己在call之前压一次
所以为了提高效率 直接JMP
下面是NotifyObserver的代码:
{
if (ObserverContainer.find(message) != ObserverContainer.end())
{
Observer * o = ObserverContainer[message];
o -> Action();
}
CallWindowProc(m_OldMsgProc, hwnd, message, wParam, lParam);
}
在这里我是将挂接的消息和消息处理函数做了一个mapping
所以我先去ObserverContainer里面查找这个消息是否被挂接了 如果不存在 则表示不需要处理这个消息
如果找到则表示需要处理 我们取出Observer的对象指针 它上面有一个虚方法叫Action 直接调用即可
下面再把Observer的注册代码贴出来:
{
ObserverContainer[msg] = o;
}