首页
社区
课程
招聘
[原创]给windows XP记事本添加“自动保存”功能
发表于: 2009-2-1 16:34 36204

[原创]给windows XP记事本添加“自动保存”功能

2009-2-1 16:34
36204

【文章作者】: 给windows XP记事本添加“自动保存”功能
【文章作者】:  newjueqi  
【作者邮箱】: zengjiansheng1@126.com
【作者QQ号】: 190678908
【作者博客】: http://blog.csdn.net/newjueqi
【使用工具】: OD, LordPE,eXeScope
【操作平台】: XP-SP2
【作者声明】: 本人平时一般的文字处理都是用记事本(用Word好像大材小用了),电脑自从拿去大修后有时候会莫名其妙的重启,弄得经常重写(本人常常忘记保存^-^),于是想给记事本增加类似于Word的自动保存功能,以图个方便。失误之处敬请诸位大侠赐教!
 
       具体的思路:其实自动保存功能就是程序隔一段时间保存一次,而记事本程序是自带了保存功能的,所以只要编程安装一个记时器,每隔一段时间模拟点击“保存菜单”,就能实现自动保存,最后退出时把记时器撤销。

首先,用eXeScope给记事本增加两个菜单项“自动保存”“关闭自动保存”,如图1所示:
 
  图1

       其中,“自动保存”的菜单ID是8 ,“关闭自动保存” 的菜单ID是13。
完成后的菜单选项如图2所示:
          
                      图2
       本次修改要用到3个window API函数: SetTimer, KillTimer , SendMessageA, 这3个API没包括在记事本的输入表里,所以要用LordPE 在输入表里增加这3个API。
     用LordPE打开记事本程序,依次选择Direction->输入表的“…”按钮¬->点击右键->add import,出现如图3的界面,就可以在输入表里增加新的API
     
      图3
完成后就新增了一个区段Slivana如图4,放的就是新的IID数据,如图4所示
             
               图4
       添加完新的API后又有一个新的问题了,那就是要确定新的API地址,这个在PEDIY的过程中是要自己解决的,而平时是编译器帮忙的。获得API地址有两个方法:
(1)直接查看新增的IID数据,载入程序后在命令行打入d 1013000,如图5所示:
           
                       图5
从IAT与INT的对应关系可得
SetTimer的地址=[0101302C ]
MessageBoxA的地址=[ 01013030]
KillTimer的地址=[01013034 ]

(2) 在OD中,按Ctrl+N组合键,就能看到API地址的位置,如图6的红框部分所示


      图6

       由于PEDIY一般都是在默认的窗口过程前先处理我们定义的消息,所以接下来分析一下记事本程序的流程,看看记事本的窗口过程地址。众所周知,在一个标准的窗口程序中,必须要注册窗口类,函数原型如下:

ATON RegisterClassEx(CONST WNDCLASSEX *Ipwcx);

其中WNDCLASSEX *Ipwcx是个结构体指针,定义如下:
WNDCLASSEX STRUCT DWORD 
       cbSize DWORD ? 
       style DWORD ? 
       lpfnWndProc DWORD ? 
       cbClsExtra DWORD ? 
       cbWndExtra DWORD ? 
       hInstance DWORD ? 
       hIcon DWORD ? 
       hCursor DWORD ? 
       hbrBackground DWORD ? 
       lpszMenuName DWORD ? 
       lpszClassName DWORD ? 
       hIconSm DWORD ? 
WNDCLASSEX ENDS 

     lpfnWndProc就是窗口过程的地址,所以我们只需要在RegisterClassEx下中断,就能知道窗口过程的地址,在OD中如图7所示:
 
    图7
从图7可看到1003429就是系统默认的窗口过程的地址。

    在开始写代码前我们先简单回顾一下的windows的窗口过程函数和一般函数调用的过程,因为我们在后面的实际编码过程中必须要对这两个方面的知识有一定的了解。

窗口过程的原型为
wondowPrco proc hwnd,uMsg, wParam, lParam

hwnd: 窗口的句柄,可通告CreateWindowEx函数的返回值获取
uMsg: 消息的标识,一般以WM开头
wParam, lParam: 消息的两个参数

     当我们点击某个菜单后就产生了WM_COMMAND消息,对于菜单引发的的WM_COMMAND消息, lParam 的值为0,wParam的低16位是命令ID,高16位是通知码,菜单消息的通知码是0。

    当调用一个_stdcall函数的时候,首先是函数的参数按从右到左的顺序入栈,然后是函数的返回地址入栈,一般函数调用的开始有“push ebp  mov ebp,esp”这句,所以我们进入窗口过程后就能得到类似于图8的堆栈结构
 
  图8
0006FDE0  /0006FE0C    ; ebp的值
0006FDE4  |77D18734   ; 返回到 user32.77D18734,返回地址值
0006FDE8  |00100618    ;窗口的句柄
0006FDEC  |00000111    ;WM_COMMAND的16进制值,可查阅WinUser.h文件获得
0006FDF0  |00000008    ;wParam,“自动保存”的菜单ID是8
0006FDF4  |00000000    ;lParam,

       由此可知,[ebp+c]是消息的类型,[ebp+10]是wParam参数,获取菜单的
ID就需要知道wParam参数。
  
       另外还有一个问题要解决,SetTimer和KillTimer这两个API函数的参数中必须要传入窗口句柄作为参数,但窗口句柄怎么找呢?我们知道,在用CreateWindowExW函数创建窗口后会返回一个窗口句柄,在OD里对CreateWindowExW下断点,中断后代码如图9:

      图9

从红框部分可知,窗口句柄保存在地址[1009830]。

    我们要模拟点击“保存”菜单,可以通过SendMessageW函数向窗口过程发送菜单消息,SendMessageW的原型如下:
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);

参数:

hWnd就是窗口句柄,从上面分析可知,保存在[1009830]地址上。
Msg是要发送的消息类型,查阅WinUser.h文件可知,WM_COMMAND为0x111
wParam的低16位是命令ID,高16位是通知码,菜单消息的通知码是0,通过eXeScope可查知“保存”的菜单ID 为3,所以wParam参数为3。
lParam,对于菜单消息来说,为0。

    接下来就是实际的代码,本人选择在0100874F处添加代码,所以要把图7红框部分的1003429改为0100874F。

下面是写入的代码

0100874F   .  55            push    ebp
01008750   .  8BEC          mov     ebp, esp
01008752   .  60            pushad  
01008753   .  817D 0C 11010>cmp     dword ptr [ebp+C], 111  ;先比较消息的类型,看是否WM_COMMAND, 如果是WM_COMMAND的话再检查wParam参数进一步确定是哪个菜单的消息
0100875A   .  75 0C         jnz     short 01008768    ;如果不是WM_COMMAND消息的话就检查是否WM_TIMER或WM_CLOSE消息
0100875C   .  837D 10 08    cmp     dword ptr [ebp+10], 8  ;比较菜单的ID号,看是否“自动保存”的菜单ID
01008760   .  74 1C         je      short 0100877E    ;不是的话就跳去检查其它的消息类型
01008762   .  837D 10 0D    cmp     dword ptr [ebp+10], 0D    ;比较菜单的ID号,看是否“关闭自动保存”的菜单ID
01008766   .  74 48         je      short 010087B0    ;不是的话就跳去检查其它的消息类型
01008768   >  817D 0C 13010>cmp     dword ptr [ebp+C], 113  ;比较消息的类型是否是WM_TIMER消息,响应定时器消息
0100876F   .  74 5F         je      short 010087D0    ;不是的话就跳去函数的结束清场工作
01008771   .  837D 0C 10    cmp     dword ptr [ebp+C], 10  ;比较消息的类型是否是WM_CLOSE消息,闭关时要先撤销定时器
01008775   .  74 39         je      short 010087B0    ;不是的话就跳去函数的结束清场工作
01008777   >  61            popad
01008778   .  5D            pop     ebp
01008779   .^ E9 ABACFFFF   jmp     01003429  ;跳回原来记事本的消息循环
0100877E   >  C605 20890001>mov     byte ptr [1008920], 1  ;标记已使用自动保存功能
01008785   .  6A 00         push    0          ; /Timerproc = NULL
01008787   .  68 60EA0000   push    0EA60      ; |Timeout = 60000. ms
0100878C   .  6A 01         push    1           ; |TimerID = 1
0100878E   .  FF35 30980001 push    dword ptr [1009830]     ; |hWnd =  [1009830],在CreateWindowExW后保存窗口句柄的地址
01008794   .  FF15 2C300101 call    dword ptr [<&user32.SetTimer>]   ; \SetTimer
0100879A   .  6A 00         push    0                 ; /Style = MB_OK|MB_APPLMODAL
0100879C   .  68 00DB0001   push    0100DB00         ; |Title = "Note"
010087A1   .  68 10DB0001   push    0100DB10          ; |Text = "AutoSave Start!"
010087A6   .  6A 00         push    0               ; |hOwner = NULL
010087A8   .  FF15 30300101 call    dword ptr [<&user32.MessageBoxA>>; \MessageBoxA  ;提示已使用自动保存功能
010087AE   .^ EB C7         jmp     short 01008777  ;跳去函数结束清场
010087B0   >  803D 20890001>cmp     byte ptr [1008920], 0  ;先比较是否已启动定时器
010087B7   .^ 74 BE         je      short 01008777    ;如果没启动定时器就跳去函数结束清场
010087B9   .  C605 20890001>mov     byte ptr [1008920], 0  ;把启动定时器的标记恢复
010087C0   .  6A 01         push    1                 ; /TimerID = 1
010087C2   .  FF35 30980001 push    dword ptr [1009830]    ; |hWnd,在CreateWindowExW后保存窗口句柄的地址
010087C8   .  FF15 34300101 call    dword ptr [<&user32.KillTimer>]  ; \KillTimer,撤销定时器
010087CE   .^ EB A7         jmp     short 01008777  ;跳去函数结束清场
010087D0   >  6A 00         push    0                  ; /lParam = 0
010087D2   .  6A 03         push    3                ; |wParam = 3 ,“保存”菜单的ID号
010087D4   .  68 11010000   push    111              ; |Message = WM_COMMAND
010087D9   .  FF35 30980001 push    dword ptr [1009830]      ; |hWnd ,在CreateWindowExW后保存窗口句柄的地址
010087DF   .  FF15 40120001 call    dword ptr [<&USER32.SendMessageW>; \SendMessageW  ;发送点击菜单“保存”的信号给记事本窗口,实现保存功能。
010087E5   .^ EB 90         jmp     short 01008777    ;跳去函数结束清场

----------------------------------------------------------------------------------------------------------------------------------------
在家乡的惯例中,今天年初七是“众人生日”,祝愿各位happy birthday  


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (83)
雪    币: 2362
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
沙发
pediy
很详细
学习了
2009-2-1 16:38
0
雪    币: 232
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
顶.....学习了~
2009-2-1 16:38
0
雪    币: 36
活跃值: (588)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
学习了!
感谢楼主
2009-2-1 16:51
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼主是真的 PEDIYER,赞一个。
今天年初七是“众人生日”,亦称“人胜节”、“人庆节”、“人口日”、“人七日”等。传说女蜗初创世,造世上生灵万物,第一日造鸡,二日狗,三日猪,四日羊,五日牛,六日马,七日人,于第七天造出了人,所以这一天是人类的生日。汉朝开始有人日节俗,魏晋后开始重视。古代人日有戴“人胜”的习俗,人胜是一种头饰,又叫彩胜,华胜,从晋朝开始有剪彩为花、剪彩为人,或镂金箔为人来贴屏风,也戴在头发上。此外还有登高赋诗的习俗。唐代之后,更重视这个节日。每至人日,皇帝赐群臣彩缕人胜,又登高大宴群臣。如果正月初七天气晴朗,则主一年人口平安,出入顺利。
2009-2-1 17:19
0
雪    币: 251
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
6
学习了
2009-2-1 17:28
0
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
7
原来如此,学习了
2009-2-1 17:40
0
雪    币: 251
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
8
在家乡,鸡生日的年初一和猪生日的年初三对鸡肉和猪肉照吃不误
2009-2-1 17:48
0
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
9
你家乡哪儿的?
2009-2-1 17:49
0
雪    币: 251
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
10
广东某个偏僻小镇,以前自称“鱼米之乡”,现在也连小鱼都看不到
2009-2-1 17:50
0
雪    币: 443
活跃值: (200)
能力值: ( LV9,RANK:1140 )
在线值:
发帖
回帖
粉丝
11
嗯,离我家很近!!!!!!!!!
2009-2-1 17:55
0
雪    币: 251
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
12
书生家乡在哪?难道是同乡
2009-2-1 17:57
0
雪    币: 399
活跃值: (38)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
13
看到这里我还以为是生肖那个1鸡2狗3猪呢,结果后面对不上,前面的0猴也没有
支持PEDIY
2009-2-1 17:57
0
雪    币: 245
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
楼主好强~学习了~!谢谢分享!
2009-2-1 18:17
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
很不错的一篇文章、学习了。
2009-2-1 18:25
0
雪    币: 479
活跃值: (25)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
16
记事本又造就了一篇精华,佩服!
2009-2-1 23:24
0
雪    币: 208
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
[QUOTE=;]...[/QUOTE]
跨平台失败,2000
2009-2-2 01:56
0
雪    币: 251
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
18
2000是没测试过,本人没2000的虚拟机
如果查到原因请告知一声,不胜感激!
2009-2-2 08:41
0
雪    币: 182
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
真的比较强大啊,o(∩_∩)o...。感觉自己要学习的还有很多啊!
2009-2-2 10:14
0
雪    币: 245
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
百看不厌~~~顶~
2009-2-2 11:07
0
雪    币: 349
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
今天年初七是“众人生日”,亦称“人胜节”、“人庆节”、“人口日”、“人七日”等。传说女蜗初创世,造世上生灵万物,第一日造鸡,二日狗,三日猪,四日羊,五日牛,六日马,七日人,于第七天造出了人,所以这一天是人类的生日。汉朝开始有人日节俗,魏晋后开始重视。古代人日有戴“人胜”的习俗,人胜是一种头饰,又叫彩胜,华胜,从晋朝开始有剪彩为花、剪彩为人,或镂金箔为人来贴屏风,也戴在头发上。此外还有登高赋诗的习俗。唐代之后,更重视这个节日。每至人日,皇帝赐群臣彩缕人胜,又登高大宴群臣。如果正月初七天气晴朗,则主一年人口平安,出入顺利。

原来如此,看看楼主的精彩文章。
2009-2-2 11:50
0
雪    币: 254
活跃值: (126)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
22
2000需要填写完整的user32.dll,不要省略扩展名
2009-2-2 12:08
0
雪    币: 251
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
23
原来如此,又学习了
2009-2-2 17:26
0
雪    币: 224
活跃值: (157)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
感谢楼主的分享,新年快乐!
2009-2-3 15:32
0
雪    币: 209
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
为什么要模拟按菜单,直接call保存代码不更好?
2009-2-3 17:57
0
游客
登录 | 注册 方可回帖
返回
//