增加你的记事本--给记事本加上文本预览功能
现在记事本是越来越鸡肋了,随便找个文本编辑器都能把它给替代了,为了增加记事本在我的硬盘上的存活时间,我决定给它增加点特色功能。现在PHOTOSHOP等图形处理软件都能在图像预览,不妨我也给记事本加上个文本预览...
一般的打开/保存文件对话框是这样的(图1):

再看记事本的打开/保存文件对话框(图2):

多了一个编码的选项。这说明记事本定制了模板。在OPENFILENAME结构中:
Flags 字段如果指明OFN_ENABLEHOOK|OFN_ENABLETEMPLATE这两个选项则可以自行定制文件对话框的行为,包括消息处理等操作。
lpfnHook 字段指向的是消息处理的过程。
思路:
明白了上面这两点,思路就明确了,我们在lpfnHook指向的过程中找到处理WM_NOTIFY消息的地方,在记事本处理这个消息之前先跳转到我们事先准备好的流程中,在我们自己的流程里面进行文本预览的操作,完成之后再跳回去让记事本继续处理,这样就完成了整个过程。
操作流程:
1.先找到lpfnHook指向的过程地址。OD载入记事本(建议先从系统目录复制一份出来再进行操作,以防不测),在命令行插件中下断点:bp GetOpenFileNameW (记事本是用UNICODE方式编译的,断这个API就行了),F9运行记事本,用记事本打开一个文件。会在浏览文件的时候被OD断在系统领空。这时查看堆栈窗口,数据如下:
0007FB48 01002D89 /CALL 到 GetOpenFileNameW 来自 NOTEPAD.01002D83
0007FB4C 0100A680 \pOpenFileName = NOTEPAD.0100A680
01002D3D |. 68 80A60001 push 0100A680 ; /pOpenFileName = NOTEPAD.0100A680
01002D42 |. A3 B0A60001 mov dword ptr [100A6B0], eax ; |
01002D47 |. C705 8CA60001>mov dword ptr [100A68C], 0100A5E0 ; |
01002D51 |. C705 BCA60001>mov dword ptr [100A6BC], 010013C4 ; |UNICODE "txt"
01002D5B |. C705 B4A60001>mov dword ptr [100A6B4], 881064 ; |
01002D65 |. C705 98A60001>mov dword ptr [100A698], 1 ; |
01002D6F |. C705 C8A60001>mov dword ptr [100A6C8], 010013A0 ; |UNICODE "NpEncodingDialog"
01002D79 |. C705 C4A60001>mov dword ptr [100A6C4], 01002452 ; |
01002D83 |. FF15 D8120001 call dword ptr [<&comdlg32.GetOpenFil>; \GetOpenFileNameW
01002D89 |. 85C0 test eax, eax
01002D3D |. 68 80A60001 push 0100A680
01002D79 |. C705 C4A60001>mov dword ptr [100A6C4], 01002452 ; |
010025EB |> \817F 08 A6FDF>cmp dword ptr [edi+8], -25A ; Case 4E (WM_NOTIFY) of switch 0100246C
010025F2 |. 0F85 01010000 jnz 010026F9
010025F8 |. 8D85 F4FDFFFF lea eax, dword ptr [ebp-20C]
BOOL NEAR CALLBACK HandleNotify(HWND hDlg,LPOFNOTIFY pofn)
{
WCHAR wsFilePath[MAX_PATH] = {0};
WCHAR wsFileTitle[MAX_FNAME_LEN] = {0};
switch(pofn->hdr.code)
{
case CDN_SELCHANGE: //文件名选择更改消息
{
CommDlg_OpenSave_GetFilePath(GetParent(hDlg), wsFilePath, MAX_PATH); //获取选择的文件名(完整路径名称)
GetFileTitle(wsFilePath, wsFileTitle, MAX_FNAME_LEN);
SetDlgItemText(hDlg, IDC_ST_FILENAME, wsFileTitle);
DWORD dwFileAttr = GetFileAttributes(wsFilePath);
if (!(dwFileAttr & FILE_ATTRIBUTE_DIRECTORY)) //如果选中的是文件
{
BY_HANDLE_FILE_INFORMATION stFileInfo;
HANDLE hFile = CreateFile(wsFilePath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
GetFileInformationByHandle(hFile, &stFileInfo);
DWORD dwMapSize = stFileInfo.nFileSizeLow > 1024?1024:stFileInfo.nFileSizeLow;
if (!dwMapSize) //如果文件大小为零
{
SetDlgItemText(hDlg, IDC_EDTPREV, TEXT(""));
break;
}
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwMapSize, NULL);
CloseHandle(hFile);
LPWSTR lpVoid = (LPWSTR)MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, dwMapSize);
CloseHandle(hFileMap);
LPSTR lpMem;
INT nCount;
if (0xFEFF == lpVoid[0]) //如果是UNICODE文本文件
{
nCount = WideCharToMultiByte(CP_ACP, 0, lpVoid + 1, dwMapSize/sizeof(WCHAR) - 1, NULL, 0, NULL, NULL);
lpMem = (LPSTR)VirtualAlloc(NULL, nCount + 1, MEM_COMMIT, PAGE_READWRITE);
WideCharToMultiByte(CP_ACP, 0, lpVoid + 1, dwMapSize/sizeof(WCHAR), lpMem, nCount, NULL, NULL);
}
else
{
nCount = dwMapSize + sizeof(CHAR);
lpMem = (LPSTR)VirtualAlloc(NULL, nCount, MEM_COMMIT, PAGE_READWRITE);
lpMem[nCount] = '\0';
memcpy(lpMem, lpVoid, nCount);
}
UnmapViewOfFile(lpVoid);
for (INT i = 0; i < (nCount - 1); i++)
{
if (lpMem[i] == 0)
lpMem[i] = 0x20;
}
SetDlgItemTextA(hDlg, IDC_EDTPREV, (LPSTR)lpMem);
VirtualFree(lpMem, nCount, MEM_RELEASE);
}
else if (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY) //如果是目录
{
SetDlgItemText(hDlg, IDC_EDTPREV, TEXT(""));
}
}
break;
}
return TRUE;
}
[培训]科锐逆向工程师培训第53期2025年7月8日开班!