本文是给记事本菜单添加一项:总在最前,通过修改消息处理来实现功能,所以第一步就是要找到windows的消息处理函数,再修改使其处理我们添加的消息,从而达到我们的目的。

首先让我们了解一个函数:

SetWindowPos
  函数功能:该函数改变一个子窗口,弹出式窗口式顶层窗口的尺寸,位置和Z序。子窗口,弹出式窗口,及顶层窗口根据它们在屏幕上出现的顺序排序、顶层窗口设置的级别最高,并且被设置为Z序的第一个窗口。
 

  函数原型:BOOL SetWindowPos(HWND hWnd,HWND hWndlnsertAfter,int X,int Y,int cx,int cy,UNIT.Flags);

首先用资源修改工具给记事本添加一个菜单项 ID可以随便 我用的是1C十进制就是28
知道了函数原型和添加了菜单项目后我们可以开始工作了.
用OD打开记事本 下断 bp RegisterClassExW  F9运行 然后断了下来
观察当前堆栈
0007FDE4   01004556  /CALL 到 RegisterClassExW 来自 NOTEPAD.01004550
0007FDE8   0007FDF0  \pWndClassEx = 0007FDF0
0007FDEC   77D49B69  USER32.LoadCursorW
0007FDF0   00000030
0007FDF4   00000000
0007FDF8   01003429  NOTEPAD.01003429
0007FDFC   00000000
0007FE00   00000000
0007FE04   01000000  NOTEPAD.01000000
0007FE08   00980761
0007FE0C   00010013
0007FE10   00000006
0007FE14   00000001
0007FE18   01009020  UNICODE 

 呵呵 01003429就是我们要的地址,为什么是这个地址呢 用MSDN看一个RegisterClassExW的原型就有答案了
Enter 来到01003429可以看到这里就是消息处理函数的入口,OD已经在注释里面给我们指出了每一个case了,我们来找个合适的位置处理我们的消息吧  ,拉着滚动条向下可以看到一个一个的case 和消息处理
哈哈 我找了这个位置
01002EA5   . /E9 BD040000   JMP NOTEPAD.01003367
01002EAA   > |83FF 1A       CMP EDI,1A
01002EAD   . |0F8F 9D580000 JG NOTEPAD.01008750
01002EB3   . |0F84 55010000 JE NOTEPAD.0100300E
01002EB9   . |83EF 16       SUB EDI,16
01002EBC   . |0F84 F4000000 JE NOTEPAD.01002FB6

这里有个和1A判断的 而我们的ID是1C 所以很接近读了一下代码发现可以开始了 我们的1C是大于1A的 所以只要修改
01002EAD   . /0F8F 9D580000 JG NOTEPAD.01008750

这个跳转 让它跳到我们的代码就好了 OK  注意:这里消息放在EDI里面
我们就在记事本的空白地方找个位置写我们的代码吧我是从1008750开始的 
下面是我的代码
01008747      00            DB 00
01008748      00            DB 00
01008749      00            DB 00
0100874A   .  00000000      DD 00000000     ;这个位置是用来记录当前的状态的
0100874E      00            DB 00
0100874F      00            DB 00
01008750   >  83FF 1C       CMP EDI,1C     ;比较是不是我们要的消息
01008753   .  74 05         JE SHORT NOTEPAD.0100875A  ;如果是就用我们的代码来处理消息
01008755   .^ E9 BFA8FFFF   JMP NOTEPAD.01003019       ;不是就回到原来的跳转位置
0100875A   >  52            PUSH EDX        ;要用它来做临时寄存器用所以先入栈    
0100875B   .  8B15 4A870001 MOV EDX,DWORD PTR DS:[100874A]   ;把当前的状态放进去
01008761   .  6A 13         PUSH 13                          ;参数压栈
01008763   .  6A 00         PUSH 0
01008765   .  6A 00         PUSH 0
01008767   .  6A 00         PUSH 0
01008769   .  6A 00         PUSH 0
0100876B   .  83FA 00       CMP EDX,0                        ;比较当前的状态如果是在最上面状态就取消不是就使他在最上面
0100876E   .  75 0A         JNZ SHORT NOTEPAD.0100877A       ;0不是最上面  1最上面
01008770   .  6A FF         PUSH -1
01008772   .  6A 01         PUSH 1
01008774   .  8F05 4A870001 POP DWORD PTR DS:[100874A]       ;修改状态标记
0100877A   >  74 0A         JE SHORT NOTEPAD.01008786
0100877C   .  6A FE         PUSH -2
0100877E   .  6A 00         PUSH 0
01008780   .  8F05 4A870001 POP DWORD PTR DS:[100874A]       ;修改状态标记
01008786   >  FF35 30980001 PUSH DWORD PTR DS:[1009830]              ; 记事本的hWnd
0100878C   .  FF15 1A400101 CALL DWORD PTR DS:[<&USER32.SetWindowPos>; call  
01008792   .  5A            POP EDX                          ;和前面入栈对照 自己压的自己出
01008793   .^ E9 6DA4FFFF   JMP NOTEPAD.01002C05             ;执行完毕后返回

然后在OD的反汇编窗口点右键-->复制到可执行文件-->右键保存文件(全部)-->选择你要保存的文件名-->保存
工作结束
注意事项:
其实SetWindowsPos这个函数记事本本身是没有的 所以有两种方法来使用它 
1:用LoadPE等工具给它添加这个函数 该函数在USER32.DLL里面
2:使用下面的方法
  push USER32.DLL
  call GetModuleHandleA
  push SetWindowsPos
  push eax
  call GetProcAddress
  call eax  ;eax就是我们要的函数SetWindowPos了 (不要像我这么写无法运行的 你要自己写)
 

到这里本该结束了,但是还有句话想说,为程序添加某个功能其实实现方法不止一种,在这里我也就不介绍了,大家可以去网上搜相关的资料,再自己研究总结,相信大家会做得比我好!