作者:任岩松中学阮赢毅

如果转载,请署名作者。

猜想1:通过窗口子类化操作

验证:不可行,因为子类化操作还是需要窗口接收消息,仍会被断,被断后函数代码也会导向处理消息代码,所以没有达到绕过消息断点的作用。

猜想2:通过钩子操作

初步设想:先用SetWindowsHookEx截获消息,然后将截获的消息分发到原来要去发到的目的窗口,修改msg参数(即窗口处理函数第二个函数,比如:SetWindowsHookEX一下键盘,然后通过SendMessage(hwnd,Msg,键盘码,0)来进行操作,再通过在窗口函数上设置对自定义的Msg的处理,实际等同于对WM_KEYDOWN或WM_KEYUP的处理。

问题:但是OLLBGY消息断点是如何实现的呢?如果消息断点也是通过建立钩子而得以实现,根据钩子的安装特点:先安装的在后,后安装的在前,那么调试器的消息断点在我执行自己的钩子函数之前,那么仍然会被断掉

验证:通过自己创建一个窗口,按上面所说的钩子操作,结果不会被断,说明消息断点不是通过建立钩子而实现的
示例程序://C++编写
#include <windows.h>
  HWND HookHwnd;         //挂钩的窗口
  UINT HookMsg;          //子定义的窗口Msg
  int HookBool;          //窗口是自建的还是调用的
    WNDPROC HookProc;      //老地址
  HHOOK HookID;          //钩子标示符
    char StoreString[50];  //返回输入的字符
LRESULT  CALLBACK AvoidProc(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam){//逃脱钩子的函数
  if(lParam&0x80000000){//按一个键会产生两个事件:WM_KEYDOWN和WM_KEYUP,所以去掉一个
    if(Msg== HookMsg){//如果消息MSG等于自定义Msg,则进行下面的代码,可以通过在这里填入代码,来进行处理
static int n;                                                   //
static char HideString[50];                           
HideString[n]='*';
StoreString[n]=wParam;
n=n+1;                                                        //索引加一
SetWindowText(hwnd,HideString);             //是输入的字符显示成字符串“*”
    return 1;                    
    }
  }
return CallWindowProc ((WNDPROC)HookProc,hwnd,Msg,wParam,lParam);//调用旧窗口
  
}

LRESULT   CALLBACK MessageHook(int nCode,WPARAM wParam,LPARAM lParam){//钩子的回调函数
    if (GetFocus()==HookHwnd){//判断输入焦点是不是想逃脱断点的窗口
        if (HookBool==1){
          static int Count;//判断是否已进行过GetWindowLong
          if (Count==0){
         HookProc=(WNDPROC)GetWindowLong(HookHwnd,GWL_WNDPROC);                                          //得到以前的窗口
   HookProc = (WNDPROC)SetWindowLong(HookHwnd, GWL_WNDPROC, (LONG)AvoidProc);//窗口子类化
          Count=1;
          }
          } 
  PostMessage(HookHwnd,HookMsg,wParam,lParam);//传递消息
    } 
return 1;
}

int MessageBreakAvoid(HWND hwnd,UINT msg,int Bool){//挂钩的函数,并进行一些初始化
  HookHwnd=hwnd;                         //初始化
  HookMsg=msg;    
    HookBool=Bool;
  HookID=SetWindowsHookEx(WH_KEYBOARD,MessageHook,GetModuleHandle(NULL),GetCurrentThreadId());//进行挂钩
return 0;  
}

int UnMessageBreakAvoid(){//删除钩子
  UnhookWindowsHookEx(HookID);
return 0;  
}
本来是想把这个编写成一个类的,然后让AvoidProc函数成为虚函数,但是编成类后,总会出现错误,好像是调用约定什么的,于是就弄成这个样子了,大家有兴趣的话,可以编写个MessageBreakAvoid类,然后把AvoidProc函数弄成虚函数,这样就是一个比较标准的接口了,
后面的代码是紧接着上面的写的,是示范如何使用的,

int PASCAL  WinMain(HINSTANCE MyHinst,HINSTANCE hPrev,LPSTR CmdLine,int ShowNumber){
HWND hwndB=CreateWindow("edit","个人简介",WS_VISIBLE,20,130,950,950,NULL,NULL,MyHinst,NULL);
ShowWindow(hwndB,1);
UpdateWindow(hwndB);
MSG MyMsg;
MessageBreakAvoid(hwndB,1101,1);                     ///注意这里
while (GetMessage(&MyMsg, NULL, 0, 0))
{
TranslateMessage(&MyMsg);
DispatchMessage(&MyMsg);
}
UnMessageBreakAvoid;
return TRUE;
}
先创建个窗口,然后MessageBreakAvoid一下,就可以逃过消息断点了,MessageBreakAvoid的第二个参数是把WM_KEYDOWN或WM_KEYUP转换成的自定义消息,我这里设成1101,大家设的时候,尽量设大点,不然和windows的自己的消息码冲突就不好了,
第三个参数是用来判断是不是自建的窗口函数,如果是自建的,你可以在自己的窗口函数代码处写入对1101的处理,如果不是,你可以在AvoidProc里写入对1101的处理,我这里是调用自带"edit"所以是1..


调试结果:成功绕过OllyDbg的消息跟踪