首页
社区
课程
招聘
[旧帖] [原创]XP的记事本手工添加“置顶”功能,练手作品 0.00雪花
发表于: 2007-10-28 21:36 6639

[旧帖] [原创]XP的记事本手工添加“置顶”功能,练手作品 0.00雪花

2007-10-28 21:36
6639
【文章标题】: XP记事本增加置顶显示功能
【文章作者】: ciker
【作者邮箱】: lyq1373@sohu.com
【软件名称】: 记事本
【使用工具】: OD,stud_PE,Uedit32,eXeScope
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
    本人最近帮别人在网上买彩票,号码记在记事本上,每次总要点下记事本,记几个号,在切换到网页买号,就想最好能把
  记事本置顶,就不用这么烦了。这好最近再学习PE结构,趁机练练手,写点总结,失误之处敬请诸位大侠赐教!
  
    首先能将窗口置顶的API函数是SetWindowPos(
    HWND hWnd,             // handle to window
    HWND hWndInsertAfter,  // placement-order handle
    int X,                 // horizontal position
    int Y,                 // vertical position
    int cx,                // width
    int cy,                // height
    UINT uFlags            // window-positioning flags
    );其中hWnd是窗口句柄,
    hWndInsertAfter指定窗口风格,这里设为HWND_TOPMOST,
    X,Y,cx,cy是窗口大小,设为默认NULL,
    uFlags是窗口位置标置,设为SSP_NOSIZE|WSP_NOMOVE;
    用VC++6.0编译,反汇编后得知,HWND_TOPMOST的代码是FF,SSP_NOSIZE|WSP_NOMOVE的代码是3,其余是0;
  
    第二步在记事本上增加置顶菜单,用eXeScope打开记事本,在菜单项的查看中增加子菜单“置顶”,ID设为70,保存;
   
    第三步要使置顶功能生效,也是最关键的一步。首先用stud_PE打开记事本,发现它的USER32.dll中没有SetWindowPos函数
  ,如果要将它插入到原USER32.dll中,势必会影响其他函数的地址,增加工作量,因此最好在原IID数组中再增加一个IID
  数组来存放USER32.dll.
    1、用stud_PE查看,输入表的RVA是0x7604,Raw是0x6A04,大小是0xC8。RVA与Raw不同,要特别注意!
    2、由于原空间不够,而它的.text空间也压缩的不够再存放一个输入表,所以只好将它移到.data区,0x7E00处。用Uedit32
  选择0x6A04处0xC8大小的数据块,复制到0x7E00处(注意是覆盖原数据,粘贴时也要选择0xC8大小的块)。
    3、在刚才复制的输入表中添加新的函数。由于记事本的输入表是一个绑定输入,所以要先找到USER32.dll的SetWindowPos
  函数地址,在OD中找到函数地址是0x77D1C01B,用Uedit32写入新的输入函数项:
  dll名称USER32.dll
  Characteristics: 000092FC
  TimeDateStamp: FFFFFFFF
  ForwarderChain: FFFFFFFF
  First thunk RVA: 000092FC
  同时将目录表里的输入表RVA改成0x7E00,IID数组大小为0xDC,已修改好的IID如下所示:
  
  00007e00h: 90 79 00 00 FF FF FF FF FF FF FF FF AC 7A 00 00 ; 恲..瑉..
  00007e10h: C4 12 00 00 40 78 00 00 FF FF FF FF FF FF FF FF ; ?..@x..
  00007e20h: FA 7A 00 00 74 11 00 00 80 79 00 00 FF FF FF FF ; 鷝..t...€y..
  00007e30h: FF FF FF FF 3A 7B 00 00 B4 12 00 00 EC 76 00 00 ; :{..?..靨..
  00007e40h: FF FF FF FF FF FF FF FF 5E 7B 00 00 20 10 00 00 ; ^{.. ...
  00007e50h: B8 79 00 00 FF FF FF FF FF FF FF FF 76 7C 00 00 ; 竬..v|..
  00007e60h: EC 12 00 00 CC 76 00 00 FF FF FF FF FF FF FF FF ; ?..蘶..
  00007e70h: 08 7D 00 00 00 10 00 00 58 77 00 00 FF FF FF FF ; .}......Xw..
  00007e80h: FF FF FF FF EC 80 00 00 8C 10 00 00 F4 76 00 00 ; 靲..?..魐..
  00007e90h: FF FF FF FF FF FF FF FF 5E 82 00 00 28 10 00 00 ; ^?.(...
  00007ea0h: 54 78 00 00 FF FF FF FF FF FF FF FF 3C 87 00 00 ; Tx..<?.
  00007eb0h: 88 11 00 00 FC 92 00 00 FF FF FF FF FF FF FF FF ; ?..鼟..
  00007ec0h: E0 92 00 00 FC 92 00 00 00 00 00 00 00 00 00 00 ; 鄴..鼟..........
  00007ed0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
  00007ee0h: 55 53 45 52 33 32 2E 64 6C 6C 00 00 00 00 53 65 ; USER32.dll....Se
  00007ef0h: 74 57 69 6E 64 6F 77 50 6F 73 00 00 1B C0 D1 77 ; tWindowPos...姥w
  
  调用SetWindowPos函数的汇编代码是CAll [基址+92FC]
  
    第四步要找到窗口函数WndProc,Windows调用窗口函数时传递4个参数:hWnd为窗口句柄,message定义消息类型,wParam  和lParam参数包含附加信息。在OD中很容易看到一个Switch 0x1002BBE,它有很多case:1…303,正好和在eXeScope中看到的menu菜单的ID号相同,随便设个断点即可验证。找到WinProc的Default Case位置
0101002C05  |> /33C0       xor     eax, eax      ;  Default case of switch 01002BBE
01002C07  |. |E9 5E070000        jmp     0100336A
    在程序的.text段中找空位,在0x7B48后有段空间,转换成RVA是0x8748,所以改跳转为jmp 01008748
    下面就调用SetWindowPos函数,首先要找到窗口句柄,WinProc的第一个参数正好就是,在OD中看就在堆栈[ebp+8]中, 所以SetWindowPos函数的所有参数都有了,调用即可,调用结束后再返回Default Case,下面是具体代码:
  
01008748   > \8B45 08       mov     eax, [ebp+8]
  0100874B   .  6A 03         push    3               ; /Flags =WP_NOSIZE|SWP_NOMOVE
  0100874D   .  6A 00         push    0               ; |Height = 0
  0100874F   .  6A 00         push    0                ; |Width = 0
  01008751   .  6A 00         push    0                ; |Y = 0
  01008753   .  6A 00         push    0                ; |X = 0
  01008755   .  6A FF         push    -1               ; |InsertAfter = HWND_TOPMOST
  01008757   .  50            push    eax              ; |hWnd
  01008758   .  FF15 FC920001 call    [10092FC]  ; \SetWindowPos
  0100875E   .^ E9 07ACFFFF   jmp     0100336A
  
    所有工作结束,保存后即可运行,在“查看”菜单的子菜单中能看到“置顶”项,点击后记事本就在屏幕最前了!
  
  
  
  
  
--------------------------------------------------------------------------------
【经验总结】
    本例还有些可改动的地方,如跳转到1008748后应该再加个判断菜单ID,但由于没有什么其他的case,所以影响不大,可
  以省略
    最困难的地方是输入表的改动,由于是绑定输入表,而且RVA和Raw不同,所以容易出错
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年10月28日 21:24:12

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (3)
雪    币: 8744
活跃值: (5210)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
LZ,增加输入表可以用lordPE,方便快捷。
查找WndProc,可以在OD里下断点bp RegisterClassExW查看WNDCLASSEX结构体的lpfnWndProc成员。
2007-10-29 01:57
0
雪    币: 8744
活跃值: (5210)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
01008750   > \8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]                      ;  Cases 9,A,B,C,D,E,F,12,13,14,15,16,17,18,19,1B of switch 01003434
01008753   .  56            PUSH ESI
01008754   .  0FB675 0C     MOVZX ESI,BYTE PTR SS:[EBP+C]
01008758 > .  83FE 46       CMP ESI,46
0100875B   .  5E            POP ESI
0100875C   .^ 0F85 68AFFFFF JNZ notepad3.010036CA
01008762   .  6A 03         PUSH 3
01008764   .  6A 00         PUSH 0
01008766   .  6A 00         PUSH 0
01008768   .  6A 00         PUSH 0
0100876A   .  6A 00         PUSH 0
0100876C   .  6A FF         PUSH -1
0100876E   .  50            PUSH EAX
0100876F   .  E8 A6A80000   CALL <&USER32.SetWindowPos>
01008774   .^ E9 51AFFFFF   JMP notepad3.010036CA
2007-10-29 02:53
0
雪    币: 431
活跃值: (70)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
我也用lordPE试过,但是由于是绑定输入表,没法自动增加输入表
2007-10-29 14:34
0
游客
登录 | 注册 方可回帖
返回
//