2.1 PE结构:文件映射进内存

微软技术分享 微软最有价值专家
全栈领域优质创作者
博客专家认证
2023-11-19 10:22:32

PE结构是Windows系统下最常用的可执行文件格式,理解PE文件格式不仅可以理解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,在任何一款操作系统中,可执行程序在被装入内存之前都是以文件的形式存放在磁盘中的,在早期DOS操作系统中,是以COM文件的格式存储的,该文件格式限制了只能使用代码段,堆栈寻址也被限制在了64KB的段中,由于PC芯片的快速发展这种文件格式极大的制约了软件的发展。

为了应对这种局面,微软的工程师们就发明了新的文件格式(EXE文件),该文件格式在代码段前面增加了文件头结构,文件头中包括各种说明数据,如程序的入口地址,堆栈的位置,重定位表等,显然可执行文件的格式是操作系统工作方式的真实写照,不同的系统之间文件格式千差万别,从而导致不同系统中的可执行文件无法跨平台运行。

PE结构包含了各类结构体,DOS头,PE标识,文件头,可选头,目录结构,节表,导入表,导出表,重定位表,资源表等等,要想掌握PE结构首相要对这些表有一个整体上的认识,Windows NT 系统中可执行文件使用微软设计的新的文件格式,也就是至今还在使用的PE格式,PE文件的基本结构如下图所示:

在PE文件中,代码,已初始化的数据,资源和重定位信息等数据被按照属性分类放到不同的Section(节区/或简称为节)中,而每个节区的属性和位置等信息用一个IMAGE_SECTION_HEADER结构来描述,所有的IMAGE_SECTION_HEADER结构组成了一个节表(Section Table),节表数据在PE文件中被放在所有节数据的前面。

在PE文件中将同样属性的数据分类放在一起是为了统一描述这些数据装入内存后的页面属性,由于数据是按照属性在节中放置的,不同用途但是属性相同的数据可能被放在同一个节中,PE文件头被放置在节和节表的前面,上面介绍的是真正的PE文件,为了兼容以前的DOS系统,所以保留了DOS的文件格式,接下来笔者将带大家从最基本的读入文件开始依次实现对PE文件的解析,并使用C语言实现一个PeView结构解析器。

在解析PE文件之前,我们首先要做的则是将PE文件从磁盘中读入到内存,有两种方式可以实现,一种是通过ReadFile函数将完整的数据读入内存,该方法会消耗更多的内存资源这里并不推荐使用,第二种方式则是采用映射的模式,所谓的映射则是将一个磁盘中的部分数据读入内存,当需要使用该片区域时由操作系统动态的装载一部分,该方式也是笔者推荐的一种实现模式;

一般来说映射文件的流程是,使用CreateFile()打开一个磁盘文件,接着使用CreateFileMapping()函数创建文件的内存映像,最后使用MapViewOfFile()读取映射中的内存并返回一个句柄,后面的程序就可以通过该句柄操作打开后的文件。

CreateFile

用来创建或打开文件的API函数,它可以接受一个文件名作为输入参数,并返回一个文件句柄。文件句柄是用来标识打开的文件的唯一标识符,后续对该文件的操作需要使用这个句柄。下面是CreateFile函数的原型:

HANDLE CreateFile(
    LPCTSTR lpFileName,          // 文件名或路径
    DWORD dwDesiredAccess,       // 访问权限
    DWORD dwShareMode,           // 共享模式
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性
    DWORD dwCreationDisposition, // 创建选项
    DWORD dwFlagsAndAttributes,  // 文件属性
    HANDLE hTemplateFile         // 模板文件句柄
);

其中,各个参数的含义如下:

  • lpFileName:指向null结尾字符串的指针,该字符串是文件名或文件的路径。
  • dwDesiredAccess:一个32位的AccessMask值,用来表示对文件的访问权限。
  • dwShareMode: 一个32位的ShareMode值,它表示其他进程可以如何访问文件。
  • lpSecurityAttributes:指向SECURITY_ATTRIBUTES结构体的指针,表示安全属性。
  • dwCreationDisposition:一个32位的值,它表示对文件的创建选项如何操作。
  • dwFlagsAndAttributes:一个32位的值,用来指定文件的属性和标志。
  • hTemplateFile:可选的模板文件句柄,用来将文件属性/属性设置为其它文件的属性/属性。

函数返回值为一个文件对象的句柄,如果函数执行失败,则返回INVALID_HANDLE_VALUE(即-1)。

CreateFileMapping

用来创建文件的内存映像的API函数。它可以将一个文件映射到内存中,这样我们就可以像访问内存一样访问文件。这个函数需要传入一个文件句柄以及一个映像的大小。它返回一个句柄,表示创建的内存映像。下面是CreateFileMapping函数的原型:

HANDLE CreateFileMapping(
    HANDLE hFile,                     // 文件句柄
    LPSECURITY_ATTRIBUTES lpAttributes, // 安全属性
    DWORD flProtect,                    // 内存保护属性
    DWORD dwMaximumSizeHigh,           // 文件映像的高32位字节大小
    DWORD dwMaximumSizeLow,            // 文件映像的低32位字节大小
    LPCTSTR lpName                     // 映像名
);

其中,各个参数的含义如下:

  • hFile:要映射到内存中的文件的句柄
  • lpAttributes:指向SECURITY_ATTRIBUTES结构体的指针,它描述内存映射对象的安全性,如果为NULL,则内存映射对象不可继承。
  • flProtect:一组标志位,它们指定内存映射区域的内存保护属性;
  • dwMaximumSizeHigh:文件映像的高32位字节大小
  • dwMaximumSizeLow:文件映像的低32位字节大小
  • lpName:映像名,可以为NULL;而且,如果该参数不为空,映像对象就成为本地系统对象,可以通过名字查找映像。

函数返回值为一个文件映射对象的句柄,如果函数执行失败,返回值为NULL。

MapViewOfFile

用来读取映射中的内存的API函数。它需要传入一个映像的句柄以及一个偏移量,用来指定从哪个位置开始读取内存。该函数返回一个指向映射内存的指针,我们可以使用它来读取或修改映射内存中的数据。下面是MapViewOfFile函数的原型:

LPVOID MapViewOfFile(
    HANDLE hFileMappingObject,  // 文件映射对象的句柄
    DWORD dwDesiredAccess,      // 访问权限
    DWORD dwFileOffsetHigh,     // 文件偏移的高32位字节个数
    DWORD dwFileOffsetLow,      // 文件偏移的低32位字节个数
    SIZE_T dwNumberOfBytesToMap // 要映射到内存中的字节数
);

其中,各个参数的含义如下:

  • hFileMappingObject:文件映射对象的句柄,可以使用CreateFileMapping函数创建,表示要映射到内存中的文件或共享内存的句柄。
  • dwDesiredAccess:一个32位的AccessMask值,用来表示对内存的访问权限。可以设置为FILE_MAP_READ、FILE_MAP_WRITE、FILE_MAP_ALL_ACCESS等。
  • dwFileOffsetHigh:文件偏移的高32位字节个数。
  • dwFileOffsetLow:文件偏移的低32位字节个数。
  • dwNumberOfBytesToMap:要映射到内存中的字节数。

函数返回值为指向映射内存的指针,如果函数执行失败,则返回NULL。在使用完内存映像后,读者记得使用UnmapViewOfFile()函数来释放映像内存,使用CloseHandle()函数来关闭文件句柄和映像句柄,以便操作系统可以回收资源。

有了上述几个关键API函数那么实现内存映射功能将会变得很容易实现,直接来看一下如下代码,当程序运行后会自动将c://pe/x86.exe目录下的文件读入内存,并返回一个lpMapAddress文件句柄;

#include <iostream>
#include <Windows.h>
#include <ImageHlp.h>

#pragma comment(lib,"Imagehlp.lib")

// --------------------------------------------------
// 定义全局变量,来存储 DOS头部/NT头部/Section头部
// --------------------------------------------------
PIMAGE_DOS_HEADER DosHeader = nullptr;
PIMAGE_NT_HEADERS NtHeader = nullptr;
PIMAGE_FILE_HEADER FileHead = nullptr;
PIMAGE_SECTION_HEADER pSection = nullptr;

// --------------------------------------------------
// 读取并设置文件基址以及文件大小
// --------------------------------------------------
CHAR GlobalFilePath[2048] = { 0 }; // 保存文件路径
DWORD GlobalFileSize = 0;          // 定义文件大小
DWORD GlobalFileBase = 0;          // 保存文件的基地址
DWORD IsOpen = 0;                  // 设置文件是否已经打开

// --------------------------------------------------
// 打开文件操作
// --------------------------------------------------
HANDLE OpenPeFile(LPCSTR FileName)
{
    HANDLE hFile, hMapFile, lpMapAddress = NULL;

    hFile = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("[-] 打开文件失败 \n");
        exit(0);
    }
    GlobalFileSize = GetFileSize(hFile, NULL);
    if (GlobalFileSize != 0)
    {
        printf("[+] 已读入文件 \n");
    }

    hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, GlobalFileSize, NULL);
    if (hMapFile == NULL)
    {
        printf("[-] 创建映射对象失败\n");
        exit(0);
    }

    lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, GlobalFileSize);
    if (lpMapAddress != NULL)
    {
        // 设置读入文件基地址
        GlobalFileBase = (DWORD)lpMapAddress;

        // 获取DOS头并判断是不是一个有效的DOS文件
        DosHeader = (PIMAGE_DOS_HEADER)GlobalFileBase;
        if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
        {
            printf("[-] 文件不属于DOS结构 \n");
            exit(0);
        }

        // 获取 NT 头并判断是不是一个有效的PE文件
        NtHeader = (PIMAGE_NT_HEADERS)(GlobalFileBase + DosHeader->e_lfanew);
        if (NtHeader->Signature != IMAGE_NT_SIGNATURE)
        {
            printf("[-] 文件不属于PE结构 \n");
            exit(0);
        }

        // 判断是不是32位程序
        if (NtHeader->OptionalHeader.Magic != 0x010B)
        {
            printf("[-] 无法调试非32位PE文件\n");
            exit(0);
        }

        // 获取到文件头指针
        FileHead = &NtHeader->FileHeader;

        // 获取到节表头
        pSection = IMAGE_FIRST_SECTION(NtHeader);
    }

    return lpMapAddress;
}

int main(int argc, char * argv[])
{
    HANDLE BaseAddr = OpenPeFile("c://pe/x86.exe");
    printf("[+] 入口地址 = %x \n", BaseAddr);

    system("pause");
    return 0;
}
...全文
13 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
上传限制,共分四卷压缩。请务必下载完所有压缩包。 目录 第一篇 软件设计基础篇 第1章 软件开发起步 2 1.1 建立MFC应用程序 2 1.2 分析框架结构 4 1.2.1 框架代码文件结构 4 1.2.2 应用程序类 5 1.2.3 对话框类 6 1.2.4 添加消息响应 7 第2章 对话框应用程序 9 2.1 模态对话框 9 2.1.1 实例:使用MFC实现模态对话框 9 2.1.2 实例:使用Win32 API实现模态对话框 10 2.2 非模态对话框 12 2.2.1 实例:使用MFC实现非模态对话框 12 2.2.2 实例:使用Win32 API实现非模态对话框 13 2.3 属性对话框 14 2.3.1 实例:多页面切换程序 14 2.3.2 实例:向导对话框 16 2.4 对话框设计技巧 17 2.4.1 控件对齐与排列 17 2.4.2 设置控件逻辑顺序 18 2.5 通用对话框 19 2.5.1 实例:通用“打开”和“另存为”对话框 19 2.5.2 实例:通用“字体”对话框 22 2.5.3 实例:通用“颜色”对话框 23 第3章 基本控件 26 3.1 按钮控件 26 3.1.1 按钮CButton类 26 3.1.2 实例:按钮控件的使用方法 28 3.2 编辑框 30 3.2.1 编辑框CEdit类 30 3.2.2 实例:编辑框的使用方法 32 3.3 列表框 33 3.3.1 列表框CListBox类 33 3.3.2 实例:列表框的使用方法 35 3.4 组合框 36 3.4.1 组合框CComboxBox类 37 3.4.2 实例:组合框的使用方法 39 3.5 度条 41 3.5.1 度条CProgressCtrl类 41 3.5.2 实例:度条的使用方法 42 3.6 列表控件 44 3.6.1 列表控件CListCtrl类 44 3.6.2 实例:列表控件的使用方法 45 第4章 文档与视图 47 4.1 文档—视图结构 47 4.1.1 单文档与多文档 47 4.1.2 文档与视图体系 48 4.2 实例:单文档应用程序与文档串行化 52 第5章 GDI绘图技术 57 5.1 图形设备接口GDI 57 5.1.1 设备上下文 57 5.1.2 GDI对象 58 5.1.3 GDI绘图 58 5.2 画笔 58 5.2.1 画笔CPen类 58 5.2.2 实例:使用GDI对象CPen绘图示例 59 5.3 画刷 60 5.3.1 画刷CBrush类 60 5.3.2 实例:使用GDI对象CBrush绘图示例 61 5.4 位图 63 5.4.1 位图CBitmap 63 5.4.2 实例:使用GDI对象CBitmap示例 64 第6章 键盘与鼠标消息 67 6.1 键盘消息 67 6.1.1 键盘消息的类型 67 6.1.2 实例:响应键盘消息示例 68 6.1.3 模拟键盘消息 70 6.1.4 实例:模拟键盘消息示例 71 6.2 鼠标消息 72 6.2.1 鼠标消息的类型 72 6.2.2 实例:处理鼠标消息 73 6.2.3 实例:模拟鼠标消息 74 第二篇 软件设计综合应用篇 第7章 网络通信基础 80 7.1 网络模型 80 7.1.1 OSI参考模型 80 7.1.2 TCP/IP参考模型 81 7.2 基础协议 82 7.2.1 IP协议 82 7.2.2 TCP协议 83 7.2.3 UDP协议 84 7.2.4 ICMP协议 85 7.3 套接字编程 85 7.3.1 函数介绍 85 7.3.2 实例:Ping程序 88 7.3.3 实例:网络嗅探器 92 7.4 服务器与客户端模型 96 7.4.1 实例:TCP服务端和客户端程序 96 7.4.2 实例:UDP服务器和客户端程序 100 7.5 实例:使用分层服务提供者LSP截取网络数据包 103 7.5.1 服务提供者接口(SPI) 103 7.5.2 设计实例 103 7.5.3 枚举协议目录 106 7.5.4 LSP的安装与卸载 108 7.5.5 分层服务提供者(LSP) 113 第8章 密码学算法 118 8.1 数据加密标准(DES) 118 8.1.1 算法描述 118 8.1.2 初始置换与逆初始置换 119 8.1.3 生成子密钥 120 8.1.4 f函数的执行流程 121 8.1.5 解密过程 122 8.1.6 实例:DES算法加密解密演示 123 8.2 国际数据加密算法(IDEA) 131 8.2.1 算法描述 131 8.2.2 生成子密钥 133 8.2.3 实例:IDEA算法加密解密演示 134 8.3 Blowfish算法 139 8.3.1 算法描述 139 8.3.2 生成子密钥和S盒 141 8.3.3 实例:Blowfish算法加密解密演示 141 8.4 公钥加密算法(RSA) 146 8.4.1 算法描述 146 8.4.2 实例:RSA加密解密演示软件 147 第9章 多媒体技术 151 9.1 多媒体控件 151 9.1.1 实例:使用Animation控件播放AVI文件 151 9.1.2 实例:使用Windows Media Player控件播放多媒体文件 152 9.1.3 实例:使用Real Player控件播放多媒体文件 153 9.2 屏幕截图 154 9.2.1 位图 154 9.2.2 实例:屏幕截图 155 9.3 屏幕录像 157 9.3.1 实现原理 157 9.3.2 实例:屏幕录像 158 第10章 数据库技术 161 10.1 设置ODBC数据源 161 10.1.1 ODBC数据源 161 10.1.2 使用ODBC管理器设置Access数据源 162 10.2 MFC ODBC数据库编程 163 10.2.1 MFC ODBC概述 163 10.2.2 实例:使用MFC ODBC访问数据库 164 10.3 MFC DAO数据库编程 169 10.3.1 MFC DAO概述 169 10.3.2 实例:使用MFC DAO访问 数据库 169 第11章 综合实例开发 174 11.1 实例:Huffman编码软件 174 11.1.1 Huffman算法原理 174 11.1.2 具体实现 175 11.2 实例:八数码游戏 178 11.2.1 八数码游戏算法介绍 178 11.2.2 具体实现 179 11.3 实例:游戏寻路算法A* 183 11.3.1 A*算法原理 183 11.3.2 二叉堆在A*中的应用 184 11.3.3 具体实现 186 11.4 实例:“连连看”游戏辅助工具 190 11.4.1 “连连看”算法原理 190 11.4.2 具体实现 191 11.5 实例:“对对碰”游戏辅助工具 196 11.5.1 “对对碰”算法原理 196 11.5.2 具体实现 197 11.6 实例:拼音输入法 199 11.6.1 设计实例 200 11.6.2 拼音字典存储结构—Trie树 200 11.6.3 单字联想 205 11.7 实例:Windows二级文件系统 209 11.7.1 设计实例 209 11.7.2 具体实现 211 11.8 实例:手柄测试器 214 11.8.1 DirectInput手柄输入 214 11.8.2 设计实例 216 第三篇 Windows系统程序设计篇 第12章 程与线程 222 12.1 程 222 12.1.1 原理介绍 223 12.1.2 创建程 223 12.1.3 实例:创建程 226 12.2 线程 227 12.2.1 原理介绍 227 12.2.2 创建线程 229 12.2.3 实例:创建线程 229 12.3 枚举程/线程信息 231 12.3.1 实例:使用PSAPI示例 231 12.3.2 实例:使用ToolHelpAPI示例 233 12.3.3 实例:使用Native API示例 235 第13章 内存管理 239 13.1 虚拟内存 239 13.1.1 程虚拟地址空间 239 13.1.2 实例:查看虚拟内存状态 240 131.3 实例:演示虚拟内存的“保留—提交”特性 243 13.1.4 实例:游戏内存修改器 245 13.2 内存映射文件 249 13.2.1 内存映射文件的原理 249 13.2.2 实例:文件分割器 250 第14章 程间通信 254 14.1 消息传递机制 254 14.1.1 消息传递 254 14.1.2 实例:使用WM_COPYDATA消息传递数据 254 14.2 共享内存 256 14.2.1 共享内存的原理 256 14.2.2 实例:使用共享内存示例 257 14.3 管道和邮槽 259 14.3.1 管道和邮槽通信原理 259 14.3.2 实例:使用匿名管道重定向程序输出 261 14.3.3 实例:命名管道示例 263 14.3.4 实例:邮槽通信示例 266 14.4 剪贴板 267 14.4.1 剪贴板通信机制 267 14.4.2 实例:使用剪贴板实现程间通信示例 269 第15章 线程同步 275 15.1 原子访问 275 15.1.1 多线程访问共享数据问题 275 15.1.2 互锁系列函数 276 15.2 关键代码段 277 15.2.1 基本原理 277 15.2.2 实例:多线程环境下的数据共享 278 15.3 内核对象与等待函数 280 15.3.1 内核对象 280 15.3.2 等待函数 281 15.4 事件内核对象 283 15.4.1 基本原理 283 15.4.2 实例:使用事件内核对象示例 284 15.5 等待定时器内核对象 285 15.5.1 基本原理 285 15.5.2 实例:使用等待定时器的APC机制 287 15.6 信标内核对象 288 15.6.1 基本原理 288 15.6.2 实例:使用信标内核对象示例 289 15.7 互斥内核对象 291 15.7.1 基本原理 292 15.7.2 实例:使用互斥内核对象示例 292 第16章 动态链接库 295 16.1 DLL基础 295 16.1.1 DLL的隐式链接 295 16.1.2 DLL的显示加载 296 16.2 编写动态链接库 297 16.2.1 入口函数DllMain 297 16.2.2 实例:编写DLL实现导出变量、函数、类 298 16.3 线程本地存储器(TLS) 301 16.3.1 静态TLS和动态TLS 301 16.3.2 实例:使用静态TLS示例 303 16.3.3 实例:使用动态TLS示例 304 第17章 结构化异常处理 306 17.1 SEH的概念、特性 306 17.2 SEH的基本使用方法 307 17.2.1 结束异常程序 307 17.2.2 异常处理程序 310 17.2.3 顶层异常处理 313 17.3 VC++编译器级SEH的具体实现 313 17.3.1 SEH相关数据结构的介绍 314 17.3.2 异常处理链结构图 315 17.3.3 实例:单嵌套异常块演示程序 316 17.3.4 实例:多嵌套异常块演示程序 318 17.3.5 VC++编译器级异常帧结构 320 17.3.6 VC中的顶层异常处理 320 17.3.7 VC搜索异常处理程序流程 322 第18章 可执行文件格式 324 18.1 PE文件格式 324 18.1.1 PE文件头 324 18.1.2 可选文件头 325 18.1.3 区块表 327 18.1.4 输入表 328 18.1.5 输出表 329 18.1.6 资源表 330 18.1.7 重定位表 332 18.1.8 绑定输入表 332 18.2 综合应用 333 18.2.1 实例: PE文件资源查看器 333 18.2.2 实例: 为应用程序添加Nag窗口 337 第19章 模块注入与函数挂接技术 341 19.1 模块注入 341 19.1.1 添加导入表项 342 19.1.2 远程线程技术 344 19.1.3 实例:使用远程线程实现模块注入 345 19.1.4 异步过程调用(APC) 346 19.1.5 实例:使用APC实现模块注入 347 19.2 挂接API 349 19.2.1 重定向API 350 19.2.2 实例:重定向API MessageBoxA示例 350 19.2.3 古老的API HOOK 353 19.2.4 实例:HOOK API示例 354 19.2.5 Detours Hook 356 19.2.6 实例:使用detour库实现挂接API示例 357 19.3 钩子 359 19.3.1 钩子的基本原理 359 19.3.2 钩子类型 360 19.3.3 实例:全局鼠标钩子示例 366 19.3.4 实例:全局键盘钩子示例 369 19.3.5 实例:使用局部CBT钩子示例 370 19.3.6 实例:使用低级键盘钩子示例 371 19.4 反注入技术 372 19.4.1 实例:使用调试钩子屏蔽全局钩子 372 19.4.2 实例:检测注入模块 374 19.4.3 实例:使用DLL_THREAD_ATTACH阻止远程线程 377 19.4.4 实例:使用挂钩LoadLibraryExW屏蔽全局钩子 379 附录 光盘源码实例 381
上传限制,共分四卷压缩。请务必下载完所有压缩包。 目录 第一篇 软件设计基础篇 第1章 软件开发起步 2 1.1 建立MFC应用程序 2 1.2 分析框架结构 4 1.2.1 框架代码文件结构 4 1.2.2 应用程序类 5 1.2.3 对话框类 6 1.2.4 添加消息响应 7 第2章 对话框应用程序 9 2.1 模态对话框 9 2.1.1 实例:使用MFC实现模态对话框 9 2.1.2 实例:使用Win32 API实现模态对话框 10 2.2 非模态对话框 12 2.2.1 实例:使用MFC实现非模态对话框 12 2.2.2 实例:使用Win32 API实现非模态对话框 13 2.3 属性对话框 14 2.3.1 实例:多页面切换程序 14 2.3.2 实例:向导对话框 16 2.4 对话框设计技巧 17 2.4.1 控件对齐与排列 17 2.4.2 设置控件逻辑顺序 18 2.5 通用对话框 19 2.5.1 实例:通用“打开”和“另存为”对话框 19 2.5.2 实例:通用“字体”对话框 22 2.5.3 实例:通用“颜色”对话框 23 第3章 基本控件 26 3.1 按钮控件 26 3.1.1 按钮CButton类 26 3.1.2 实例:按钮控件的使用方法 28 3.2 编辑框 30 3.2.1 编辑框CEdit类 30 3.2.2 实例:编辑框的使用方法 32 3.3 列表框 33 3.3.1 列表框CListBox类 33 3.3.2 实例:列表框的使用方法 35 3.4 组合框 36 3.4.1 组合框CComboxBox类 37 3.4.2 实例:组合框的使用方法 39 3.5 度条 41 3.5.1 度条CProgressCtrl类 41 3.5.2 实例:度条的使用方法 42 3.6 列表控件 44 3.6.1 列表控件CListCtrl类 44 3.6.2 实例:列表控件的使用方法 45 第4章 文档与视图 47 4.1 文档—视图结构 47 4.1.1 单文档与多文档 47 4.1.2 文档与视图体系 48 4.2 实例:单文档应用程序与文档串行化 52 第5章 GDI绘图技术 57 5.1 图形设备接口GDI 57 5.1.1 设备上下文 57 5.1.2 GDI对象 58 5.1.3 GDI绘图 58 5.2 画笔 58 5.2.1 画笔CPen类 58 5.2.2 实例:使用GDI对象CPen绘图示例 59 5.3 画刷 60 5.3.1 画刷CBrush类 60 5.3.2 实例:使用GDI对象CBrush绘图示例 61 5.4 位图 63 5.4.1 位图CBitmap 63 5.4.2 实例:使用GDI对象CBitmap示例 64 第6章 键盘与鼠标消息 67 6.1 键盘消息 67 6.1.1 键盘消息的类型 67 6.1.2 实例:响应键盘消息示例 68 6.1.3 模拟键盘消息 70 6.1.4 实例:模拟键盘消息示例 71 6.2 鼠标消息 72 6.2.1 鼠标消息的类型 72 6.2.2 实例:处理鼠标消息 73 6.2.3 实例:模拟鼠标消息 74 第二篇 软件设计综合应用篇 第7章 网络通信基础 80 7.1 网络模型 80 7.1.1 OSI参考模型 80 7.1.2 TCP/IP参考模型 81 7.2 基础协议 82 7.2.1 IP协议 82 7.2.2 TCP协议 83 7.2.3 UDP协议 84 7.2.4 ICMP协议 85 7.3 套接字编程 85 7.3.1 函数介绍 85 7.3.2 实例:Ping程序 88 7.3.3 实例:网络嗅探器 92 7.4 服务器与客户端模型 96 7.4.1 实例:TCP服务端和客户端程序 96 7.4.2 实例:UDP服务器和客户端程序 100 7.5 实例:使用分层服务提供者LSP截取网络数据包 103 7.5.1 服务提供者接口(SPI) 103 7.5.2 设计实例 103 7.5.3 枚举协议目录 106 7.5.4 LSP的安装与卸载 108 7.5.5 分层服务提供者(LSP) 113 第8章 密码学算法 118 8.1 数据加密标准(DES) 118 8.1.1 算法描述 118 8.1.2 初始置换与逆初始置换 119 8.1.3 生成子密钥 120 8.1.4 f函数的执行流程 121 8.1.5 解密过程 122 8.1.6 实例:DES算法加密解密演示 123 8.2 国际数据加密算法(IDEA) 131 8.2.1 算法描述 131 8.2.2 生成子密钥 133 8.2.3 实例:IDEA算法加密解密演示 134 8.3 Blowfish算法 139 8.3.1 算法描述 139 8.3.2 生成子密钥和S盒 141 8.3.3 实例:Blowfish算法加密解密演示 141 8.4 公钥加密算法(RSA) 146 8.4.1 算法描述 146 8.4.2 实例:RSA加密解密演示软件 147 第9章 多媒体技术 151 9.1 多媒体控件 151 9.1.1 实例:使用Animation控件播放AVI文件 151 9.1.2 实例:使用Windows Media Player控件播放多媒体文件 152 9.1.3 实例:使用Real Player控件播放多媒体文件 153 9.2 屏幕截图 154 9.2.1 位图 154 9.2.2 实例:屏幕截图 155 9.3 屏幕录像 157 9.3.1 实现原理 157 9.3.2 实例:屏幕录像 158 第10章 数据库技术 161 10.1 设置ODBC数据源 161 10.1.1 ODBC数据源 161 10.1.2 使用ODBC管理器设置Access数据源 162 10.2 MFC ODBC数据库编程 163 10.2.1 MFC ODBC概述 163 10.2.2 实例:使用MFC ODBC访问数据库 164 10.3 MFC DAO数据库编程 169 10.3.1 MFC DAO概述 169 10.3.2 实例:使用MFC DAO访问 数据库 169 第11章 综合实例开发 174 11.1 实例:Huffman编码软件 174 11.1.1 Huffman算法原理 174 11.1.2 具体实现 175 11.2 实例:八数码游戏 178 11.2.1 八数码游戏算法介绍 178 11.2.2 具体实现 179 11.3 实例:游戏寻路算法A* 183 11.3.1 A*算法原理 183 11.3.2 二叉堆在A*中的应用 184 11.3.3 具体实现 186 11.4 实例:“连连看”游戏辅助工具 190 11.4.1 “连连看”算法原理 190 11.4.2 具体实现 191 11.5 实例:“对对碰”游戏辅助工具 196 11.5.1 “对对碰”算法原理 196 11.5.2 具体实现 197 11.6 实例:拼音输入法 199 11.6.1 设计实例 200 11.6.2 拼音字典存储结构—Trie树 200 11.6.3 单字联想 205 11.7 实例:Windows二级文件系统 209 11.7.1 设计实例 209 11.7.2 具体实现 211 11.8 实例:手柄测试器 214 11.8.1 DirectInput手柄输入 214 11.8.2 设计实例 216 第三篇 Windows系统程序设计篇 第12章 程与线程 222 12.1 程 222 12.1.1 原理介绍 223 12.1.2 创建程 223 12.1.3 实例:创建程 226 12.2 线程 227 12.2.1 原理介绍 227 12.2.2 创建线程 229 12.2.3 实例:创建线程 229 12.3 枚举程/线程信息 231 12.3.1 实例:使用PSAPI示例 231 12.3.2 实例:使用ToolHelpAPI示例 233 12.3.3 实例:使用Native API示例 235 第13章 内存管理 239 13.1 虚拟内存 239 13.1.1 程虚拟地址空间 239 13.1.2 实例:查看虚拟内存状态 240 131.3 实例:演示虚拟内存的“保留—提交”特性 243 13.1.4 实例:游戏内存修改器 245 13.2 内存映射文件 249 13.2.1 内存映射文件的原理 249 13.2.2 实例:文件分割器 250 第14章 程间通信 254 14.1 消息传递机制 254 14.1.1 消息传递 254 14.1.2 实例:使用WM_COPYDATA消息传递数据 254 14.2 共享内存 256 14.2.1 共享内存的原理 256 14.2.2 实例:使用共享内存示例 257 14.3 管道和邮槽 259 14.3.1 管道和邮槽通信原理 259 14.3.2 实例:使用匿名管道重定向程序输出 261 14.3.3 实例:命名管道示例 263 14.3.4 实例:邮槽通信示例 266 14.4 剪贴板 267 14.4.1 剪贴板通信机制 267 14.4.2 实例:使用剪贴板实现程间通信示例 269 第15章 线程同步 275 15.1 原子访问 275 15.1.1 多线程访问共享数据问题 275 15.1.2 互锁系列函数 276 15.2 关键代码段 277 15.2.1 基本原理 277 15.2.2 实例:多线程环境下的数据共享 278 15.3 内核对象与等待函数 280 15.3.1 内核对象 280 15.3.2 等待函数 281 15.4 事件内核对象 283 15.4.1 基本原理 283 15.4.2 实例:使用事件内核对象示例 284 15.5 等待定时器内核对象 285 15.5.1 基本原理 285 15.5.2 实例:使用等待定时器的APC机制 287 15.6 信标内核对象 288 15.6.1 基本原理 288 15.6.2 实例:使用信标内核对象示例 289 15.7 互斥内核对象 291 15.7.1 基本原理 292 15.7.2 实例:使用互斥内核对象示例 292 第16章 动态链接库 295 16.1 DLL基础 295 16.1.1 DLL的隐式链接 295 16.1.2 DLL的显示加载 296 16.2 编写动态链接库 297 16.2.1 入口函数DllMain 297 16.2.2 实例:编写DLL实现导出变量、函数、类 298 16.3 线程本地存储器(TLS) 301 16.3.1 静态TLS和动态TLS 301 16.3.2 实例:使用静态TLS示例 303 16.3.3 实例:使用动态TLS示例 304 第17章 结构化异常处理 306 17.1 SEH的概念、特性 306 17.2 SEH的基本使用方法 307 17.2.1 结束异常程序 307 17.2.2 异常处理程序 310 17.2.3 顶层异常处理 313 17.3 VC++编译器级SEH的具体实现 313 17.3.1 SEH相关数据结构的介绍 314 17.3.2 异常处理链结构图 315 17.3.3 实例:单嵌套异常块演示程序 316 17.3.4 实例:多嵌套异常块演示程序 318 17.3.5 VC++编译器级异常帧结构 320 17.3.6 VC中的顶层异常处理 320 17.3.7 VC搜索异常处理程序流程 322 第18章 可执行文件格式 324 18.1 PE文件格式 324 18.1.1 PE文件头 324 18.1.2 可选文件头 325 18.1.3 区块表 327 18.1.4 输入表 328 18.1.5 输出表 329 18.1.6 资源表 330 18.1.7 重定位表 332 18.1.8 绑定输入表 332 18.2 综合应用 333 18.2.1 实例: PE文件资源查看器 333 18.2.2 实例: 为应用程序添加Nag窗口 337 第19章 模块注入与函数挂接技术 341 19.1 模块注入 341 19.1.1 添加导入表项 342 19.1.2 远程线程技术 344 19.1.3 实例:使用远程线程实现模块注入 345 19.1.4 异步过程调用(APC) 346 19.1.5 实例:使用APC实现模块注入 347 19.2 挂接API 349 19.2.1 重定向API 350 19.2.2 实例:重定向API MessageBoxA示例 350 19.2.3 古老的API HOOK 353 19.2.4 实例:HOOK API示例 354 19.2.5 Detours Hook 356 19.2.6 实例:使用detour库实现挂接API示例 357 19.3 钩子 359 19.3.1 钩子的基本原理 359 19.3.2 钩子类型 360 19.3.3 实例:全局鼠标钩子示例 366 19.3.4 实例:全局键盘钩子示例 369 19.3.5 实例:使用局部CBT钩子示例 370 19.3.6 实例:使用低级键盘钩子示例 371 19.4 反注入技术 372 19.4.1 实例:使用调试钩子屏蔽全局钩子 372 19.4.2 实例:检测注入模块 374 19.4.3 实例:使用DLL_THREAD_ATTACH阻止远程线程 377 19.4.4 实例:使用挂钩LoadLibraryExW屏蔽全局钩子 379 附录 光盘源码实例 381
第1章 再谈计算机内存访问 1 1.1 引言 1 1.2 内存管理概述 1 1.2.1 虚拟内存 1 1.2.2 CPU工作模式 2 1.2.3 逻辑、线性和物理地址 3 1.2.4 存储器分页管理机制 3 1.2.5 线性地址到物理地址的转换 4 1.3 虚拟内存访问 5 1.3.1 获取系统信息 5 1.3.2 在应用程序中使用虚拟内存 6 1.3.3 获取虚存状态 7 1.3.4 确定虚拟地址空间的状态 8 1.3.5 改变内存页面保护属性 9 1.3.6 行一个程的内存读写 10 1.4 文件内存映射 11 1.4.1 内存映射API函数 11 1.4.2 用内存映射在多个应用程序之间共享数据 13 1.4.3 用内存映射文件读取大型文件 18 1.5 深入认识指针的真正含义 21 .1.5.1 指针的真正本质 21 1.5.2 用指针行应用程序之间的通信 22 1.6 本章小结 26 参考文献 27 第2章 再谈PE文件结构 28 2.1 引言 28 2.2 PE文件格式概述 28 2.2.1 PE文件结构布局 28 2.2.2 PE文件内存映射 30 2.2.3 Big-endian和Little-endian 31 2.2.4 3种不同的地址 31 2.3 PE文件结构 32 2.3.1 MS-DOS头部 32 2.3.2 IMAGE_NT_HEADER头部 33 2.3.3 IMAGE_SECTION_HEADER头部 36 2.4 如何获取PE文件中的OEP 36 2.4.1 通过文件读取OEP值 37 2.4.2 通过内存映射读取OEP值 38 2.4.3 读取OEP值方法的测试 39 2.5 PE文件中的资源 40 2.5.1 查找资源在文件中的起始位置 40 2.5.2 确定PE文件中的资源 41 2.6 一个修改PE可执行文件的完整实例 43 2.6.1 如何获得MessageBoxA代码 43 2.6.2 把MessageBoxA()代码写入PE文件的完整实例 45 2.7 本章小结 53 参考文献 53 第3章 程之间通信概述及初级技术 54 3.1 引言 54 3.2 程通信概述 55 3.2.1 Windows程间标准通信技术的发展 55 3.2.2 应用程序与程 56 3.2.3 程之间通信的类型 56 3.3 使用自定义消息通信 57 3.3.1 通过自定义消息实现程间通信的方法 57 3.3.2 通过自定义消息实现程间通信的实例 58 3.4 使用WM_COPYDATA消息通信 60 3.4.1 通过WM_COPYDATA消息实现程间通信的方法 60 3.4.2 通过WM_COPYDATA消息实现程间通信的实例 61 3.5 使用内存读写函数和内存映射文件通信 62 3.5.1 使用内存映射文件通信的方法 62 3.5.2 使用内存读写函数实现程间通信的方法 62 3.5.3 使用内存读写函数实现程间通信的实例 63 3.6 使用动态链接库通信 64 3.6.1 DLL概述 64 3.6.2 使用DLL通信的方法 65 3.6.3 使用DLL通信的实例 66 3.7 使用Windows剪贴板通信 67 3.7.1 使用剪贴板实现程间通信的方法 68 3.7.2 使用剪贴板实现程间通信的实例 68 3.8 使用动态数据交换(DDE)通信 70 3.8.1 使用DDE技术通信原理 70 3.8.2 如何使用DDEML编写程序 71 3.8.3 使用DDE通信的实例 72 3.9 本章小结 77 参考文献 77 第4章 使用消息管道、邮槽和套接字通信 78 4.1 引言 78 4.2 如何用命名管道程间通信 78 4.2.1 命名管道函数 79 4.2.2 命名管道服务端与客户端之间通信的实现流程 80 4.2.3 命名管道服务端与客户端之间通信的实例 81 4.3 如何用邮槽程间通信 85 4.3.1 用邮槽程间通信的步骤 85 4.3.2 邮槽服务器端与客户端之间通信的实例 86 4.4 如何用Windows套接字程间通信 90 4.4.1 套接字分类 90 4.4.2 流式套接字编程流程 91 4.4.3 套接字调用基本函数 92 4.4.4 Winsock程序设计 95 4.4.5 一个通用套接字类 96 4.4.6 套接字服务器端与客户端间通信的实例 101 4.5 本章小结 106 参考文献 106 第5章 使用LPC和RPC通信 107 5.1 引言 107 5.2 接口定义语言(IDL)简介 107 5.3 使用本地过程调用(LPC)通信 108 5.3.1 使用LPC通信方法介绍 108 5.3.2 使用LPC通信的实例 110 5.4 使用远程过程调用(RPC)通信 117 5.4.1 RPC运行机制 117 5.4.2 RPC 绑定模式和属性 118 5.4.3 RPC传输(Transport) 118 5.4.4 如何编写RPC应用程序 119 5.4.5 使用RPC通信的实例 120 5.5 本章小结 128 参考文献 128 第6章 使用组件模型通信 129 6.1 引言 129 6.2 COM/DCOM模型概述 129 6.2.1 COM/DCOM的特点 129 6.2.2 COM/DCOM组件模型分类 130 6.3 使用组件对象模型(COM/DCOM)通信 131 6.3.1 使用COM/DCOM通信方法介绍 131 6.3.2 基于DCOM实现远程会话的实例 136 6.4 本章小结 147 参考文献 147 第7章 程的创建、控制和隐藏 148 7.1 引言 148 7.2 常见的几种创建程的方法 148 7.2.1 使用WinExec() 函数 148 7.2.2 使用ShellExecute()和ShellExecuteEx()函数 149 7.2.3 使用CreateProcess()函数 151 7.2.4 使用OLE激活服务程序 154 7.3 如何获得程句柄 155 7.3.1 获得一个程的句柄 155 7.3.2 提升程权限级别 156 7.4 如何实现当前程的枚举 158 7.4.1 通过系统快照实现当前程的枚举 158 7.4.2 通过psapi.dll提供的API函数实现当前程的枚举 160 7.4.3 通过wtsapi32.dll提供的API函数实现当前程的枚举 162 7.4.4 通过ntdll.dll提供的API函数实现当前程的枚举 163 7.5 如何终止程 164 7.5.1 如何终止本程 165 7.5.2 如何终止外部程 165 7.5.3 终止程的实例 165 7.6 如何隐藏程(注入代码) 166 7.6.1 基本原理 166 7.6.2 使用CreateRemoteThread()隐藏DLL 167 7.6.3 使用CreateRemoteThread()直接注入API函数代码 173 7.6.4 使用Windows内存映射文件注入代码 174 7.6.5 使用特洛伊DLL注入代码 174 7.6.6 使用注册表注入DLL 175 7.6.7 使用程序挂钩的方法注入代码 175 7.7 本章小结 175 参考文献 176 第8章 应用程序的静态挂钩 177 8.1 引言 177 8.2 使用C/C++语言提取可执行程序代码 177 8.2.1 在C/C++中使用内联汇编 177 8.2.2 如何使用C/C++语言提取可执行程序代码 179 8.3 如何对PE文件加壳 182 8.3.1 PE文件的加壳方法 182 8.3.2 向PE文件中静态注入代码的完整实例 183 8.4 如何实现文件脱壳 191 8.5 本章小结 192 参考文献 192 第9章 应用程序的动态挂钩 193 9.1 动态挂钩概述 193 9.2 使用Windows钩子函数挂钩 194 9.2.1 Windows钩子函数 194 9.2.2 具体实例 195 9.3 替换原API函数入口挂钩 198 9.3.1 如何替换原API函数入口实现挂钩 198 9.3.2 通用的替换原API函数入口挂钩类 199 9.3.3 使用JMP法编写的挂钩实例 201 9.4 替换IAT中的函数地址行挂钩 202 9.4.1 如何替换IAT中的函数地址实现挂钩 202 9.4.2 通用的替换IAT中的函数地址挂钩类 203 9.4.3 使用IAT法编写的挂钩实例 207 9.5 替换Windows消息处理函数实现挂钩 208 9.5.1 Windows消息处理函数及其替换 209 9.5.2 替换Windows消息处理函数实现挂钩的实例 210 9.6 钩子DLL文件的装载 214 9.7 本章小结 216 参考文献 216 第10章 数据的编码和解码实例 217 10.1 引言 217 10.2 游程编码 218 10.2.1 CX游程压缩方法 218 10.2.2 BI_RLE8压缩方法 218 10.2.3 BI_RLE压缩方法 218 10.2.4 缩位压缩方法(Packbits) 219 10.3 Huffman编码 219 10.3.1 Huffman编码原理 219 10.3.2 Huffman编码过程 220 10.4 算术编码 221 10.4.1 算术编码算法 221 10.4.2 算术解码算法 222 10.5 LZW压缩算法 222 10.5.1 LZW压缩算法原理 223 10.5.2 用VC++实现LZW压缩算法 225 10.6 Base64编码 236 10.6.1 Base64算法原理 236 10.6.2 Base64算法的实现 238 10.7 本章小结 241 参考文献 242 第11章 可执行文件的捆绑和分离 243 11.1 引言 243 11.2 捆绑方式分类 243 11.2.1 结合式捆绑 243 11.2.2 功能式捆绑 245 11.3 文件捆绑相关技术 245 11.3.1 文件捆绑工具及实现 245 11.3.2 木马程序与捆绑 246 11.3.3 文件自身操作特点分析 246 11.4 文件属性的获取和伪装 248 11.4.1 文件属性的获取和更改 248 11.4.2 一个获取文件基本属性类 249 11.4.3 可执行程序自删除的实现 251 11.4.4 如何获取其他应用程序的图标 254 11.4.5 如何改变窗口的图标 255 11.5 被捆绑文件分离后的运行及自分解文件原理 256 11.5.1 异步执行分解法的实现 256 11.5.2 同步执行分解法的实现 256 11.5.3 自动分解法的实现 257 11.6 一个捆绑机(BindHider)软件的设计 258 11.6.1 BindHider的设计 258 11.6.2 BindHider的源代码 259 11.7 一种制作自分解文件的方法 263 11.7.1 母体程序的制作 264 11.7.2 自分解文件的制作 266 11.8 本章小结 267 参考文献 268 第12章 可执行文件的分割和合并 269 12.1 引言 269 12.2 文件分割方式 269 12.2.1 考虑文件格式的分割 269 12.2.2 设置子文件大小的分割 270 12.2.3 具有自合并功能的文件分割 271 12.2.4 依赖文件存放位置的分割 271 12.2.5 依赖磁盘大小的分割 271 12.3 如何使用多线程 272 12.3.1 线程的创建和终止 272 12.3.2 线程的控制函数 273 12.3.3 线程的通信 273 12.4 文件的简单分割与合并 274 12.4.1 文件的简单分割 274 12.4.2 文件的简单合并 275 12.5 用多线程文件的分割与合并的实例 277 12.5.1 文件的分割与合并方案设计 277 12.5.2 用多线程文件分割 279 12.5.3 用多线程文件合并 282 12.6 分割后文件自动合并的方案设计 286 12.6.1 控制程序的制作 286 12.6.2 用于文件自合并的控制程序的制作 287 12.6.3 一种生成自合并文件的分割软件制作 289 12.7 本章小结 292 参考文献 292 第13章 多线程下载和断点续传 293 13.1 引言 293 13.2 使用FTP行多线程下载和断点续传 293 13.2.1 FTP协议简介 293 13.2.2 FTP的工作模式 295 13.2.3 FTP协议多线程下载和断点续传的实现 295 13.2.4 实例 306 13.3 使用HTTP行多线程下载和断点续传 307 13.3.1 HTTP协议简介 307 13.3.2 HTTP协议的内部操作过程 308 13.3.3 HTTP协议多线程下载和断点续传的实现 311 13.3.4 实例 321 13.4 BT下载简介 323 13.4.1 BT下载与一般下载的区别 323 13.4.2 BT种子 324 13.4.3 BT的下载过程 324 13.5 本章小结 324 参考文献 325 第14章 带附件的电子邮件发送剖析 326 14.1 引言 326 14.2 电子邮件的发送方法 326 14.3 用WinSock实现SMTP协议 327 14.3.1 SMTP协议 327 14.3.2 SMTP的实现 328 14.4 邮件格式化 335 14.4.1 邮件主体格式化 335 14.4.2 邮件附件格式化 338 14.4.3 邮件格式化 341 14.5 发送电子邮件实例 346 14.6 本章小结 347 参考文献 347 第15章 特洛伊木马与反木马技术 348 15.1 引言 348 15.2 常见的木马种类 349 15.3 木马的载入方式 350 15.4 木马采用的伪装方法 351 15.5 Windows 2K/XP中无法删除文件的常用解决办法 352 15.6 一种木马病毒的检测技术 353 15.7 本章小结 358

479

社区成员

发帖
与我相关
我的任务
社区描述
微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。
windowsmicrosoft 企业社区
社区管理员
  • 微软技术分享
  • 郑子铭
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。

予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。

试试用AI创作助手写篇文章吧