Windows中文件
在Windows编程中,少不了打交道的肯定是文件,但是我们绝大多数接触到的都是文件的表象。
在Windows中有各种各样的文件,大致有:
- exe dll:可执行文件
- txt:文本文件
- jpg bmp gif: 图像图形文件
- lnk: 快捷方式文件
- rar 7z zip iso:压缩文件
- avi mp4 rmvb 影音文件
这些文件实际行可以分为两大类,一大类是exe、dll这样的可执行文件,剩下的就是数据文件。
其实在其他操作系统中也是这样分类的,但是它们都是属于文件。
在Windows中可执行文件会较为特殊,它将会拥有执行的权利,也就是说能够通过操作系统的某一个程序进行启动,在启动的过程中要调用CreateProcess。一般的使用资源管理器启用CreateProcess函数来打来可执行文件。
而数据文件是由可执行文件来进行读取的。
所以无论是可执行文件还是数据文件,我们都可以使用CreateFile函数来打开,但是对于exe、dll文件我们一般是使用CreateProcess来打开的,这样也没问题,但是如果使用CreateProcess打开数据文件,它就会发生错误。因为在Windows中所有的可执行文件都会有一个标志,说明它是一个可执行文件。那么这个标志存放在哪里呢?它就存放在我们经常听说的PE结构里。下面我们就简单的介绍一下PE结构:
PE结构存在于exe、dll中,它包括了PE头(又分为DOS头和NT头,操作系统就是根据DOS头和NT头来辨别是否是可执行文件,所以我们可以给普通的数据文件加上PE头让它执行起来,虽然执行结果不知道是怎么!),PE头后面就是当前可执行程序的信息,这些信息会帮助操作系统进行当前程序的运行。需要注意的是可执行文件中的数据和我们上面提到的普通数据文件中的数据是不一样的,它的排列方式和功能都是不一样的。
需要注意的是:操作系统判断是否是可执行文件并不是靠文件的后缀名.exe、.dll来判断的,这些是给人看的,操作系统是根据PE结构中的一些标志位来判断是否是可执行文件的。
利用上面的特性,我们就可以简单的写一个小程序来判断一个文件是否是一个PE文件。
检查PE文件的程序实现
创建的是一个对话框应用程序。
程序的界面如下:
// CheckPEDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "CheckPE.h"
#include "CheckPEDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CCheckPEDlg 对话框
CCheckPEDlg::CCheckPEDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_CHECKPE_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CCheckPEDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_FILE_PATH, m_editFilePathCtrl);
}
BEGIN_MESSAGE_MAP(CCheckPEDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_OPEN, &CCheckPEDlg::OnBnClickedButtonOpen)
ON_BN_CLICKED(IDC_BUTTON_CHECK, &CCheckPEDlg::OnBnClickedButtonCheck)
ON_BN_CLICKED(IDC_BUTTON_MODIFY, &CCheckPEDlg::OnBnClickedButtonModify)
ON_BN_CLICKED(IDC_BUTTON_RECOVER, &CCheckPEDlg::OnBnClickedButtonRecover)
END_MESSAGE_MAP()
// CCheckPEDlg 消息处理程序
BOOL CCheckPEDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CCheckPEDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CCheckPEDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CCheckPEDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CCheckPEDlg::OnBnClickedButtonOpen()
{
CFileDialog fileDialog(TRUE);
if (fileDialog.DoModal() == IDOK)
{
m_editFilePathCtrl.SetWindowTextW(fileDialog.GetPathName());
}
}
void CCheckPEDlg::OnBnClickedButtonCheck()
{
CString strFileType = L"不是PE文件!";
CString strFilePath;
m_editFilePathCtrl.GetWindowTextW(strFilePath);
if (strFilePath.IsEmpty())
{
MessageBoxW(L"请先打开文件!");
return;
}
if (IsPEFile(strFilePath.GetString()))
{
strFileType = L"是PE文件!";
}
MessageBoxW(strFileType);
}
BOOL CCheckPEDlg::IsPEFile(const TCHAR* strFilePath)
{
BOOL bRet = FALSE;
BOOL bNeedClose = FALSE;
CFile file;
do
{
if (!file.Open(strFilePath, CFile::modeReadWrite))
{
break;
}
bNeedClose = TRUE;
IMAGE_DOS_HEADER imageDosHeader;
DWORD dwReadSize;
dwReadSize = file.Read(&imageDosHeader, sizeof(IMAGE_DOS_HEADER));
if (dwReadSize != sizeof(IMAGE_DOS_HEADER))
{
break;
}
if (imageDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
{
break;
}
IMAGE_NT_HEADERS32 imageNtHeaders32;
file.Seek(imageDosHeader.e_lfanew, CFile::begin);
dwReadSize = file.Read(&imageNtHeaders32, sizeof(IMAGE_NT_HEADERS32));
if (dwReadSize != sizeof(IMAGE_NT_HEADERS32))
{
break;
}
if (imageNtHeaders32.Signature != IMAGE_NT_SIGNATURE)
{
break;
}
bRet = TRUE;
} while (false);
if (bNeedClose)
{
file.Close();
}
return bRet;
}
void CCheckPEDlg::OnBnClickedButtonModify()
{
CString strFilePath;
m_editFilePathCtrl.GetWindowTextW(strFilePath);
if (strFilePath.IsEmpty())
{
MessageBoxW(L"请先打开文件!");
return;
}
if (IsPEFile(strFilePath.GetString()))
{
// 是PE文件,将PE头移至最尾部
BOOL bNeedClose = FALSE;
CFile file;
do
{
if (!file.Open(strFilePath, CFile::modeReadWrite))
{
break;
}
bNeedClose = TRUE;
IMAGE_DOS_HEADER imageDosHeader;
file.Read(&imageDosHeader, sizeof(IMAGE_DOS_HEADER));
IMAGE_NT_HEADERS32 imageNtHeaders32;
file.Seek(imageDosHeader.e_lfanew, CFile::begin);
file.Read(&imageNtHeaders32, sizeof(IMAGE_NT_HEADERS32));
file.SeekToEnd();
file.Write(&imageDosHeader, sizeof(IMAGE_DOS_HEADER));
file.Write(&imageNtHeaders32, sizeof(IMAGE_NT_HEADERS32));
IMAGE_DOS_HEADER newImageDosHeader = { 0 };
IMAGE_NT_HEADERS32 newImageNtHeaders32 = { 0 };
file.SeekToBegin();
file.Write(&newImageDosHeader, sizeof(IMAGE_DOS_HEADER));
file.Seek(imageDosHeader.e_lfanew, CFile::begin);
file.Write(&newImageDosHeader, sizeof(IMAGE_NT_HEADERS32));
} while (false);
if (bNeedClose)
{
file.Close();
MessageBoxW(L"面杀成功!");
}
}
}
void CCheckPEDlg::OnBnClickedButtonRecover()
{
CString strFilePath;
m_editFilePathCtrl.GetWindowTextW(strFilePath);
if (strFilePath.IsEmpty())
{
MessageBoxW(L"请先打开文件!");
return;
}
BOOL bNeedClose = FALSE;
CFile file;
do
{
if (!file.Open(strFilePath, CFile::modeReadWrite))
{
break;
}
bNeedClose = TRUE;
int i1 = sizeof(IMAGE_DOS_HEADER);
int i2 = sizeof(IMAGE_NT_HEADERS32);
file.Seek(-(i1 + i2), CFile::end);
IMAGE_DOS_HEADER imageDosHeader;
file.Read(&imageDosHeader, sizeof(IMAGE_DOS_HEADER));
IMAGE_NT_HEADERS32 imageNtHeaders32;
file.Read(&imageNtHeaders32, sizeof(IMAGE_NT_HEADERS32));
file.SeekToBegin();
file.Write(&imageDosHeader, sizeof(IMAGE_DOS_HEADER));
file.Seek(imageDosHeader.e_lfanew, CFile::begin);
file.Write(&imageNtHeaders32, sizeof(IMAGE_NT_HEADERS32));
} while (false);
if (bNeedClose)
{
file.Close();
MessageBoxW(L"复原成功!");
}
}
void CCheckPEDlg::Win32Style()
{
CString strFileType = L"不是PE文件!";
CString strFilePath;
m_editFilePathCtrl.GetWindowTextW(strFilePath);
if (strFilePath.IsEmpty())
{
MessageBoxW(L"请先打开文件!");
return;
}
HANDLE hFile = INVALID_HANDLE_VALUE;
do
{
if (strFilePath.IsEmpty())
{
MessageBoxW(L"请先打开文件!");
break;
}
hFile = CreateFile(strFilePath.GetString(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
{
break;
}
IMAGE_DOS_HEADER imageDosHeader;
DWORD dwReadSize;
::ReadFile(hFile, &imageDosHeader, sizeof(IMAGE_DOS_HEADER), &dwReadSize, nullptr);
if (dwReadSize != sizeof(IMAGE_DOS_HEADER))
{
break;
}
if (imageDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
{
break;
}
IMAGE_NT_HEADERS32 imageNtHeaders32;
if (SetFilePointer(hFile, imageDosHeader.e_lfanew, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
break;
}
::ReadFile(hFile, &imageNtHeaders32, sizeof(IMAGE_NT_HEADERS32), &dwReadSize, nullptr);
if (dwReadSize != sizeof(IMAGE_NT_HEADERS32))
{
break;
}
if (imageNtHeaders32.Signature != IMAGE_NT_SIGNATURE)
{
break;
}
strFileType = L"是PE文件!";
} while (false);
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
}
MessageBoxW(strFileType);
}
// CheckPEDlg.h : 头文件
//
#pragma once
#include "afxwin.h"
// CCheckPEDlg 对话框
class CCheckPEDlg : public CDialogEx
{
// 构造
public:
CCheckPEDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_CHECKPE_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedButtonOpen();
afx_msg void OnBnClickedButtonCheck();
CEdit m_editFilePathCtrl;
BOOL IsPEFile(const TCHAR *strFilePath);
afx_msg void OnBnClickedButtonModify();
afx_msg void OnBnClickedButtonRecover();
void Win32Style();
};
MFC中的命名方式比较好,尽量把动词放到前面。
但是很多时候MFC中的函数名和Win32中的函数名重复,在按f1查看文档的时候,有可能会出现不是我们想要找的函数,此时我们需要去MFC所对应的类中去找这个方法,所以有的时候不能直接按f1进行查询。