欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

为你的Notepad打上只属于你的烙印

为你的Notepad打上只属于你的烙印

制作目的:
1、看完了《加密与解密》二次开发那一章,需要练手巩固一下
2、毕竟是第一次二次开发,总得留下什么做纪念,不是嘛(⊙_⊙)?

选择缘由:
1、上家公司有一位BOSS逆向很牛,当时他向我们介绍逆向这门学问时,曾举过自己改造Notepad的事情(给其增加置顶与半透明功能),让人好生羡慕,所以二次开发Notepad也有纪念的意味;
2、在Ollydbg调试时,用记事本记东西比较方便,不过没有置顶、半透明功能,导致经常需要切换来切换去,比较麻烦,所以给记事本增加这些功能还是比较有意义的;

补充说明:
下面讲述时假设你已经比较熟悉PE结构,最好还看过“二次开发”那一章节;
言归正传:
我们知道二次开发有2种方式,第一种是直接修改可执行文件,在上面添加功能;第二种是为可执行文件导出扩展接口,调用我们用高级语言写的程序来实现需要的功能;因为有练手的目的,所以我们打算二者都采用;
以下是最终的效果图:



相对于原先记事本的具体改变是:
1、在菜单栏中增加了“高级”菜单
2、其中“置顶”、“透明度”及“说明”三个子菜单功能,我们是通过直接修改可执行文件完成的;
3、同时为记事本增加了DLL调用接口(插件式),方便采用高级语言二次开发,DEMO中的DLL为“高级”菜单增加一个“计算器”菜单项,点击后打开系统计算器;
4、所有需要保存的设置信息我们均采用注册表保存(如置顶及透明度配置);
要实现上述的功能,我们必须先找到合适的点能够插入我们的代码,比如调用CreateWindow的地方,又或者消息循环处理WM_COMMAND的地方,不然一切都是白搭;
首先我们先逆向下Notepad的运行流程,具体就是先对RegisterClassW以及RegisterClassExW下断点,最后运行发现断点断在RegisterClassExW函数处,接下来我们按Alt+M打开内存窗口,对Notepad的代码段01001000按F2下断点后再继续运行,此时停下来的地方就是RegisterClassExW调用结束的地方;具体如下:

01004524  |.  8D45 D0                LEA     EAX, [LOCAL.12]
01004527  |.  50                     PUSH    EAX                                                ; /pWndClassEx = 0006FDF0
01004528  |.  C745 F4 01000000       MOV     [LOCAL.3], 1                                       ; |
0100452F  |.  8975 E4                MOV     [LOCAL.7], ESI                                     ; |mynotepa.01000000
01004532  |.  C745 F8 20900001       MOV     [LOCAL.2], OFFSET mynotepa.szNotepad               ; |UNICODE "Notepad"
01004539  |.  C745 D8 29340001       MOV     [LOCAL.10], mynotepa.NPWndProc                     ; |
01004540  |.  C745 F0 06000000       MOV     [LOCAL.4], 6                                       ; |
01004547  |.  897D D4                MOV     [LOCAL.11], EDI                                    ; |
0100454A  |.  897D DC                MOV     [LOCAL.9], EDI                                     ; |
0100454D  |.  897D E0                MOV     [LOCAL.8], EDI                                     ; |
01004550  |.  FF15 D0110001          CALL    NEAR DWORD PTR DS:[<&USER32.RegisterClassExW>]     ; \RegisterClassExW
01004556  |.  66:F7D8                NEG     AX
我们从RegisterClassExW参数中可以得知消息循环处理代码在地址01003429处,函数名为NPWndProc(带debug信息就是好呀,O(∩_∩)O~);
随后我们对CreateWindowExW下断点执行,中断后按Alt+F9执行到调用结束处,具体如下:
01004667  |.  53                     PUSH    EBX                                                ; /lParam = NULL
01004668  |.  56                     PUSH    ESI                                                ; |hInst = 01001394
01004669  |.  53                     PUSH    EBX                                                ; |hMenu = NULL
0100466A  |.  53                     PUSH    EBX                                                ; |hParent = NULL
0100466B  |.  FF35 709A0001          PUSH    DWORD PTR DS:[1009A70]                             ; |Height = 19F (415.)
01004671  |.  BE 94130001            MOV     ESI, mynotepa.01001394                             ; |
01004676  |.  FF35 749A0001          PUSH    DWORD PTR DS:[1009A74]                             ; |Width = 301 (769.)
0100467C  |.  FF35 7C9A0001          PUSH    DWORD PTR DS:[1009A7C]                             ; |Y = 15B (347.)
01004682  |.  FF35 789A0001          PUSH    DWORD PTR DS:[1009A78]                             ; |X = 14C (332.)
01004688  |.  68 0000CF00            PUSH    0CF0000                                            ; |Style = WS_OVERLAPPED|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU|WS_THICKFRAME|WS_CAPTION
0100468D  |.  56                     PUSH    ESI                                                ; |WindowName = ""
0100468E  |.  68 20900001            PUSH    OFFSET mynotepa.szNotepad                          ; |Class = "Notepad"
01004693  |.  53                     PUSH    EBX                                                ; |ExtStyle = 0
01004694  |.  FF15 E0110001          CALL    NEAR DWORD PTR DS:[<&USER32.CreateWindowExW>]      ; \CreateWindowExW
0100469A  |.  8BD0                   MOV     EDX, EAX
0100469C  |.  3BD3                   CMP     EDX, EBX
稍微跟踪下这段代码,发现CreateWindow以及ShowWindow这些行为都包含在一个名为“NPInit”的函数中,相应call指令的地址是0100297B,机器码是5字节大小,具体如下:
01002974  |.  50                     PUSH    EAX                                                        ; |Arg3 = 01000000
01002975  |.  FF75 0C                PUSH    [ARG.2]                                                    ; |Arg2 = FFFFFFFF
01002978  |.  FF75 08                PUSH    [ARG.1]                                                    ; |Arg1 = 7C930228
0100297B  |.  E8 E51B0000            CALL    mynotepa.NPInit                                            ; \NPInit
01002980  |.  85C0                   TEST    EAX, EAX                                                   ;  mynotepa.01000000
01002982  |.  75 08                  JNZ     SHORT mynotepa.0100298C
01002984  |.  8975 E8                MOV     [LOCAL.6], ESI
01002987  |.  E9 B0000000            JMP     mynotepa.01002A3C
0100298C  |>  53                     PUSH    EBX
0100298D  |.  56                     PUSH    ESI                                                        ; /Flags = WINEVENT_OUTOFCONTEXT|WINEVENT_SKIPOWNTHREAD|WINEVENT_SKIPOWNPROCESS|92348
0100298E  |.  56                     PUSH    ESI                                                        ; |ThreadId = 9234B
0100298F  |.  FF15 0C110001          CALL    NEAR DWORD PTR DS:[<&KERNEL32.GetCurrentProcessId>]        ; |[GetCurrentProcessId
从中我们发现NPInit调用结束后会判断返回值是否为TRUE,如果是FALSE则表示失败,并直接退出;
因而我们可以在这里进行第一步的截获操作,使其jmp到我们指定的地方去,在那里先调用NPInit,随后判断返回值是否为TRUE;若TRUE则接着调用我们额外添加的初始化函数,用于初始化用户的置顶及透明度设置;
因为我们的主要功能是通过菜单点击调用的,所以我们还需要继续查找消息循环中WM_COMMAND消息的处理逻辑;对刚刚找到的消息循环处理过程入口下条件断点([ebp+0x0C]==WM_COMMAND)并单步跟踪,不难发现,Notepad对于WM_COMMAND的消息处理专门封装了一个名为NPCommand(01002B87)的函数来处理,同时发现该函数对于未知命令ID的处理逻辑位于地址010032ED处,具体如下:

010032CB  |.  81FF FF020000          CMP     EDI, 2FF
010032D1  |.^ 0F8E 2EF9FFFF          JLE     mynotepa.01002C05
010032D7  |.  81FF 01030000          CMP     EDI, 301
010032DD  |.  7E 14                  JLE     SHORT mynotepa.010032F3
010032DF  |.  81FF 02030000          CMP     EDI, 302
010032E5  |.  74 39                  JE      SHORT mynotepa.01003320
010032E7  |.  81FF 03030000          CMP     EDI, 303
010032ED  |.^ 0F85 12F9FFFF          JNZ     mynotepa.01002C05
010032F3  |>  8D85 DCFDFFFF          LEA     EAX, [LOCAL.137]                           ;  Cases 300,301,303 of switch 01002BBE
010032F9  |.  50                     PUSH    EAX                                        ; /lParam = 1000000
010032FA  |.  8D85 F0FDFFFF          LEA     EAX, [LOCAL.132]                           ; |
01003300  |.  50                     PUSH    EAX                                        ; |wParam = 1000000
01003301  |.  68 B0000000            PUSH    0B0                                        ; |Message = EM_GETSEL
01003306  |.  FF35 38980001          PUSH    DWORD PTR DS:[hwndEdit]                    ; |hWnd = NULL
0100330C  |.  FF15 40120001          CALL    NEAR DWORD PTR DS:[<&USER32.SendMessageW>] ; \SendMessageW
因此我们可以截获地址010032ED处的JNZ指令,使其跳转到我们扩增的命令处理处,处理完之后,我们再让其跳回至地址01002C05处,执行接下来的代码;
找到上述插入点后,接下来我们就可以放心的去打造我们所需的个性化Notepad了;
首先给Notepad增加菜单资源、对话框资源及加速键资源,建议采用VS编辑对话框资源(可视化编辑比较方便),具体如下截图所示:


在NPInit中我们发现被载入的加速键资源名为MAINACC,所以我们只需编辑名为MAINACC的加速键资源,给其增加30001(置顶),50001(计算器)二个加速键设置:

透明度设置对话框资源效果如下(其中对话框ID是30101,透明度值输出控件ID是30102,trackbar的ID是30103):

修改好上述资源后,我们接下来查看导入表中是否有我们需要的所有函数,如果没有的话,可以通过LordPE为其增加。在实际开发过程中,我们发现有如下几组函数我们需要用到,但是原导入表没有,分别列表如下(你可以现在全部添加,也可以在开发过程中逐步添加):
RegOpenKeyExW                   ADVAPI32.dll    读取注册表时用到
RegCreateKeyExW                 ADVAPI32.dll    写入注册表时用到
InitCommonControlsEx            COMCTL32.dll    透明度设置对话框中TrackBar控件用到
SetTextColor                    GDI32.dll       透明度大小显示时用到
CreateSolidBrush                GDI32.dll       透明度大小显示时用到
SetWindowPos                    USER32.dll      设置置顶时用到
SetLayeredWindowAttributes      USER32.dll      设置透明度时用到
GetSysColor                     USER32.dll      透明度大小显示时用到

为了可持续开发,我们的目标不仅仅是本次的二次开发,我们还需要为未来的第N次开发做好规划;所以在地址空间布局上我们采用增加单独段的方式去扩展,而不是缝隙法;为此我们新增一个.mynpd数据段及.mynpc代码段,同时将来布置代码时,新增代码段中的各个代码块之间的间隔布置的大一点,以免未来扩充或者改动时发现没有空间,只好jmp来jmp去,那样不管在性能还是代码可读性上都比较差一点;
布置好空间的效果如图:(其中.Slivana是LordPE增加的新的导入表段,这里先不管三七二十一重建导入表,是为了让我们新增的段位于最后,便于将来可以连续地址空间扩增代码段)


最后就是完成上述功能的全部代码改动及分析:
首先我们跳转到原先call NPInit的地方(0100297B),采用JMP 01016000指令来替换,使其跳转到我们新增的地址空间中去,然后我们在01016000地址处新增代码,具体如下:

;初始操作逻辑处理处
01016000  CALL    mynotepa.NPInit               ;先调用原先的NPInit函数
01016005  TEST    EAX, EAX
01016007  JE      mynotepa.01002980             ;如果NPInit()==FALSE,则直接返回原逻辑处
0101600D  PUSH    DWORD PTR DS:[hwndNP]         ;参数是hwndNP
01016013  CALL    mynotepa.01016401             ;InitMyNotepad:用于初始化置顶、透明度设置
01016018  PUSH    DWORD PTR DS:[hwndNP]         ;参数是hwndNP
0101601E  CALL    mynotepa.01016A00             ;调用我们插件DLL的初始化函数
01016023  JMP     mynotepa.01002980             ;处理完后再返回到原逻辑处
其中DS:[hwndNP]是Notepad窗口的句柄,在跟踪NPInit的时候,可以看到其创建完窗口后会把句柄保存到hwndNP中,我们在初始化的时候正好可以拿来用;
接下来我们修改原地址010032ED处的指令为JMP 01016050,使其跳转到我们扩展的命令处理逻辑处,具体如下:
;菜单命令逻辑处理处
01016050  LEA     EAX, DWORD PTR DS:[EDI+FFFF8AD0]      ;当前命令ID-30000
01016056  CMP     EAX, 3
01016059  JA      mynotepa.01016150                     ;如果值大于3,则说明ID的处理不在此处,我们需要调用DLL插件中的函数尝试能否处理下
0101605F  PUSH    DWORD PTR SS:[EBP+8]                  ;接下来所以命令处理函数,我们均以hwndNP为参数,所以直接在开头调用下,免去每次调用的麻烦
01016062  DEC     EAX                                   ;ID==30001,置顶
01016063  JNZ     SHORT mynotepa.0101606F
01016065  CALL    mynotepa.01016500                     ;SetTopMost:设置/取消置顶功能
0101606A  JMP     mynotepa.01003367                     ;成功处理完之后直接返回到正确处理了命令的地方
0101606F  DEC     EAX                                   ;ID==30002,透明度
01016070  JNZ     SHORT mynotepa.0101607C
01016072  CALL    mynotepa.01016600                     ;SetTransparency:弹出对话框,让用户设置透明度
01016077  JMP     mynotepa.01003367                     ;成功处理完之后直接返回到正确处理了命令的地方
0101607C  DEC     EAX                                   ;ID==30003,说明
0101607D  JNZ     mynotepa.01016141
01016083  CALL    mynotepa.01016954                     ;ShowAboutDlg:弹出说明对话框
01016088  JMP     mynotepa.01003367                     ;成功处理完之后直接返回到正确处理了命令的地方
        。
        。
        。

01016141  POP     EAX                                   ;放置这么远是为了给将来N次开发Notepad留下空间

        。
        。
        。
01016150  MOV     EAX, DWORD PTR DS:[10151F8]           ;判断DLL中MynpexCommandProc函数指针是否存在
01016155  TEST    EAX, EAX                              ;为0表明不存在
01016157  JE      mynotepa.01002C05                     ;无法处理菜单命令时,跳转回原无法处理的逻辑处
;目前demo中实现了打开计算器的功能,具体见DLL实现代码
0101615D  PUSH    DWORD PTR SS:[EBP+10]                 ;lParam
01016160  PUSH    DWORD PTR SS:[EBP+C]                  ;wParam
01016163  PUSH    DWORD PTR SS:[EBP+8]                  ;hwndNP
01016166  CALL    NEAR EAX                              ;DLL中专门用于处理菜单命令的MynpexCommandProc
01016168  TEST    EAX, EAX                              ;返回值为1则表明成功处理
0101616A  JE      mynotepa.01002C05                     ;成功处理完之后直接返回到正确处理了命令的地方
01016170  JMP     mynotepa.01003367                     ;无法处理菜单命令时,跳转回原无法处理的逻辑处
以下是上述逻辑中被调用函数的具体实现及注解:
[ebp+0x14]      lpcbData
[ebp+0x10]      lpData
[ebp+0xC]       dwType
[ebp+0x8]       lpValueName
function GetValInReg 按照用户指定的key,把数据从注册表HKEY_LOCAL_MACHINE\SOFTWARE\MyNotepad键下读取出来
[ebp-0x4]       hKey
[ebp-0x8]       dwValType
01016200  PUSH    EBP
01016201  MOV     EBP, ESP
01016203  SUB     ESP, 8
01016206  PUSH    EBX
01016207  XOR     EBX, EBX
01016209  CMP     DWORD PTR SS:[EBP+8], EBX
0101620C  JE      SHORT mynotepa.0101626B                               ;je label_failure
0101620E  CMP     DWORD PTR SS:[EBP+10], EBX
01016211  JE      SHORT mynotepa.0101626B                               ;je label_failure
01016213  CMP     DWORD PTR SS:[EBP+14], EBX
01016216  JE      SHORT mynotepa.0101626B                               ;je label_failure
01016218  LEA     EAX, DWORD PTR SS:[EBP-4]
0101621B  PUSH    EAX                                                   ;&hKey
0101621C  PUSH    1                                                     ;KEY_QUERY_VALUE
0101621E  PUSH    EBX                                                   ;0
0101621F  PUSH    mynotepa.01015000                                     ;L"SOFTWARE\\MyNotepad"
01016224  PUSH    80000002                                              ;HKEY_LOCAL_MACHINE
01016229  CALL    NEAR DWORD PTR DS:[<&ADVAPI32.RegOpenKeyExW>]         ;ADVAPI32.RegOpenKeyExW
0101622F  TEST    EAX, EAX
01016231  JNZ     SHORT mynotepa.0101626B                               ;jnz label_failure
01016233  PUSH    DWORD PTR SS:[EBP+14]                                 ;lpcbData
01016236  PUSH    DWORD PTR SS:[EBP+10]                                 ;lpData
01016239  LEA     EAX, DWORD PTR SS:[EBP-8]
0101623C  PUSH    EAX                                                   ;&dwValType
0101623D  PUSH    EBX                                                   ;0
0101623E  PUSH    DWORD PTR SS:[EBP+8]                                  ;lpValueName
01016241  PUSH    DWORD PTR SS:[EBP-4]                                  ;hKey
01016244  CALL    NEAR DWORD PTR DS:[<&ADVAPI32.RegQueryValueExW>]      ;ADVAPI32.RegQueryValueExW
0101624A  MOV     EBX, EAX
0101624C  PUSH    DWORD PTR SS:[EBP-4]                                  ;hKey
0101624F  CALL    NEAR DWORD PTR DS:[<&ADVAPI32.RegCloseKey>]           ;ADVAPI32.RegCloseKey
01016255  TEST    EBX, EBX                                              ;不为0表明查询失败情况
01016257  JNZ     SHORT mynotepa.0101626B                               ;jnz label_failure
01016259  MOV     EAX, DWORD PTR SS:[EBP-8]
0101625C  CMP     EAX, DWORD PTR SS:[EBP+C]                             ;dwValType != dwType
0101625F  JNZ     SHORT mynotepa.0101626B                               ;jnz label_failure
01016261  XOR     EAX, EAX
01016263  INC     EAX
label_return:
01016264  POP     EBX
01016265  MOV     ESP, EBP
01016267  POP     EBP
01016268  RETN    10
label_failure:
0101626B  XOR     EAX, EAX
0101626D  JMP     SHORT mynotepa.01016264                               ;jmp label_return
由于代码过长,不好排版,所以接下来的代码及注释我放到了附件“代码及注解.txt”中,同时二次开发后的Notepad及插件DLL的示例代码也均在附件中;

上传的附件 代码及注解.txt
mynotepad.zip

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功