【文章标题】: 给记事本加上透明窗体和置顶功能(通过扩展消息循环的方法)-入门级的东东
【软件名称】: 系统自带记事本
【软件大小】: 65.2KB
【下载地址】: X:\windows\notepad.exe
【加壳方式】: 无壳
【保护方式】: 无
【编写语言】: Microsoft Visual C++ 7.0
【使用工具】: OD,LordPE,ResHacker
【操作平台】: XP-Sp2
【软件介绍】: 系统自带记事本,小巧实用.
--------------------------------------------------------------------------------
【详细过程】
在开始本文时,说一下整体的思路。
整个过程的基本思想是在记事本的消息处理之前,先转到我们自己添加的消息处理函数处理相关的事件,处理完再转到原WndProc执行。自己添加的消息处理功能放在动态链接库(MyPePlug.dll)中。
说完了整体思路,下面我们分步来完成整个过程。
 (1)  用ResHacker添加一个新的菜单:
 POPUP "附加功能(&A)"
{
  MENUITEM "透明(&T)",  118   //记住这个ID号(118),在下面的MyPePlug.cpp会用到
MENUITEM "置顶(&K)",  119   //记住这个ID号(119),在下面的MyPePlug.cpp会用到
}
(2)  用OD加载记事本,在命令行输入:bp RegisterClassExW (也可能是RegisterClassExA),然后F9运行程序.此时程序会断下.看堆栈框如下(图1):

 
图1
其中0006FDF0就是注册类的结构定义,在它上面点右键,再选择“堆栈窗口跟随”,此时在堆栈中查看,就成这样了:


 
图2
其中01003429就是记事本的消息处理函数的地址了。记住这个地址,下面会用到。
然后按ALT+F9,看反汇编窗口,向上拉一段代码你会在反汇编窗口中看到下图的代码:


 
图3
记住消息处理过程的地址01004539,等一下会回过头来修改这段代码。
(3)写一个自己的消息处理函数
/////////////////////////////////////////// MyPePlug.h ///////////////////////////////////////////////////////////
#ifdef MYPEPLUG
#else
#define MYPEPLUG extern "C" __declspec(dllimport)
#endif
MYPEPLUG void LoadPlug(const DWORD reversed, 
             HWND hWnd, 
             UINT msg, 
             WPARAM wParam, 
             LPARAM lParam);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////// MyPePlug.cpp//////////////////////////////////////////////////////////////
#include <Windows.h>
#define MYPEPLUG extern "C" __declspec(dllexport)
#include "MyPePlug.h "

bool flag = true;

void MySetLayeredWindowAttributes(HWND hWnd)
{
  SetWindowLong(hWnd,GWL_EXSTYLE,
    GetWindowLong(hWnd,GWL_EXSTYLE)^0x80000);
  HINSTANCE hInst = LoadLibrary("User32.DLL"); 
  if(hInst) 
  {            
    typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);          
    MYFUNC fun = NULL;
    //取得SetLayeredWindowAttributes函数指针     
    fun=(MYFUNC)GetProcAddress(hInst, "SetLayeredWindowAttributes");
    if(fun)fun(hWnd,0,128,2);     
    FreeLibrary(hInst); 
  }
}

void LoadPlug(const DWORD reversed, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg) {
  case WM_COMMAND:
    if(wParam == 118)//这是我自己通过资源添加的一个菜单的id
  {
//由于我的vc++的sdk是老版本,所以没有SetLayeredWindowAttributes函数,如果你的sdk
//是新版本,就不用我这么麻烦了,你就可以直接调用SetLayeredWindowAttributes函数了
  MySetLayeredWindowAttributes(hWnd);
  }
  else if(wParam == 119)
  {
    if(flag)
    {
      SetWindowPos(hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);
      flag=false;
    }
    else
    {
      SetWindowPos(hWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);
      flag=true;
    }
  }
  }
}
//////////////////////////////////////////////////////////////////////////////////////(4)编译上面的源文件,生成MyPePlug.dll,把生成的dll文件放在记事本的当前目录里。
(5)用LordPE把LoadPlug函数添加到记事本程序中。(详细过程看<<加密与解密>>第三版P513) 


图4
记住ThunkRVA项的值(00013038),下面会用到.
(6) 再次用OD加载记事本,然后Ctrl+G,在对话框中输入01003429(这是在第二步要你记住的值),来到01003429.如下图(图 5):
 

图5

然后利用5个int3和mov edi,edi共7个字节,改写一个call指令:call dword ptr [1013018](共六个,后面不一个nop),这就是调用我们自己的消息处理函数.至于为什么是这样,你可以看看<<加密与解密>>第三版P513
(7)修改消息处理过程的地址,由前面的分析可以知道该地址是01004539。
再一次按Ctrl+G,在对话框中输入01004539,来到01004539处,把这处的代码“mov  dword ptr [ebp-28], 1003429”改成“mov dword ptr [ebp-28], 01003424”。
(8)在OD的反汇编窗口点右键-->复制到可执行文件-->右键保存文件(全部)-->选择你要保存的文件名-->保存。
 好了,就这样就成功的添加了你想要的功能了.
------------------------------------------------------------------------------------------
【 参考文章】 
记事本功能增加方案(http://bbs.pediy.com/showthread.php?threadid=17376 ) 
<<加密与解密>>第三版 第19章 代码的二次开发
--------------------------------------------------------------------------------------

二八年八月五日