探究Windows2000/XP原型PTE

探究Windows 2000/XP原型PTE(http://webcrazy.yeah.net) 探究Windows 2000/XP原型PTE              WebCrazy(http://webcrazy.yeah.net)    内存管理可以说是操作系统实现中最重要的环节,也是最为复杂的一环节。对于相对贫乏的内存资源,内存共享也成了一个很重要的有效手段。Windows 2000/XP在此方面的实现借助于一个称为原型PTE(Prototype PTE,PPTE)的软件机制。在《小 阅读详情
内存管理可以说是操作系统实现中最重要的环节,也是最为复杂的一环节。对于相对贫乏的内存资源,内存共享也成了一个很重要的有效手段。Windows 2000/XP在此方面的实现借助于一个称为原型PTE(Prototype PTE,PPTE)的软件机制。在《小议Windows NT/2000分页机制》中我详细的介绍了Intel X86实现分段、分页的硬件PTE工作方式。我们来回顾一下这种机制:

    假设我们的一个进程映射了从虚拟地址0xXXXXXXXX(假设位于分配粒度上)开始的4M空间,而这4M空间当前都相应的映射了实际的物理内存(鉴于Lazy evaluation等的先进思想,这种情况在Windows 2000/XP中比较少见)。我们将这4M空间分成1000块的4K(PAGE_SIZE,X86处理器决定),对于第n个4K(0<=n<1000),其虚拟地址(0xXXXXXXXX+n*4K),我们都有一个对应的硬件PTE,指出目前这4K驻留于物理内存的位置。通过由PDBR(CR3寄存器)与虚拟地址可定位这个硬件PTE(具体请参阅《小议Windows NT/2000分页机制》)。

    现在让我们来考虑这样一种情况,我们有一个文件其大小也为4M,我们知道通常我们要使用这个文件都要将它读入内存。试想同时有两个或更多的进程需读写这个文件,这就需要解决内存共享的问题。实际上就算当前只有一个进程访问这个文件,对于这种潜在的需要共享的文件,Windows 2000/XP均会事先考虑共享情况。她通过一个称为Section的内核对象来实现这样的目的。仔细想想,这种情况下内存共享决不仅仅是内存资源的充分利用,就算我们可以为每个进程各分配4M空间,但是这将导致各个进程某种时刻可能得不到这个文件的最新内容。这是非常糟糕的情况。在内部Windows 2000/XP利用原型PTE来解决这样的情况。基于硬件PTE相同的原理,对于这样一个4M的文件,在映射这个文件时,Windows 2000/XP同样的将这个文件分成1000块,每块4K(PAGE_SIZE)大小。然后从页交换区分配1000个DWORD,每个DWORD值都是原型PTE,它们组成原型PTE表。对于这个文件的第n个4K(0<=n<1000),如果当前其驻留在物理内存中的话,其对应的PPTE的Valid位(bit 0,与硬件PTE一致)为1,然后这个PPTE的Page Frame Number(PPTE的高20位)用于指示物理内存。如果当前其仍然在磁盘中的话,Valid位为0。针对这种情况,通过PPTE的高20位(PFN Entry),查找Page Frame Datbase(由MmPfnDatabase定位),通过PFN Entry的Subsection PTE(windbg中称为restore pte,《Inside Windows 2000》中称为original pte,Windows XP内部称为Subsection PTE),定位Subsection,然后通过Subsection指向的Control Area的FILE_OBJECT,与PPTE在PPTE表的偏移n,通过公式:

    PFN Entry Subsection PTE->Subsection->Control Area->FileObject + n * 4K

定位所要访问的文件偏移,这样Windows 2000/XP使用页面调入IO读入这页内容,更新PPTE表的这个PPTE。以上的这一系列定位转换算法,如Subsection PTE如何定位Subsection,我将另行介绍。上面的描述解决了一个非常重要的问题,我们不需要更新所有引用这一页面的进程的硬件PTE,因为此时所有进程的PTE均指向PPTE,我们只要更新PPTE就能达到目的。至于进程PTE如何指向PPTE,下面我会涉及到这个内容。这儿你只要有一个概念,进程的PTE为了指向PPTE,肯定是一个Invalid PTE,即bit 0为0,而且其bit 10为1(PPTE标志,具体请看我在《探寻Windows NT/2000 Copy On Write机制》列出的HARDWARE_PTE_X86结构)。

    对于PPTE,因为X86处理器没有提供这样一种方式,像处理硬件PTE一样,由CPU直接进行地址转换。Windows 2000/XP内存管理器在处理Page Fault时,通过软件机制来模拟这种实现,这可以说是硬件PTE与PPTE的一个本质区别。

    应该重点提出的是PPTE存在于页交换区(由MmPagedPoolStart与MmPagedPoolEnd指定的位置,从虚拟地址0xE1000000开始),其本身也有可能被Page Out,Windows 2000/XP通过MiCheckProtoPtePageState判断是否被Page Out,还有页交换区的起始地址0xE10000000将用于从无效PTE转化成原型PTE所在的地址,这等一下我会介绍到的。

    照例我们用SoftICE来验证一下我们前面的描述:

    :bpint e

    只要我们截获这个硬件中断,我们就知道肯定发生了Page Fault,但是我们并不能确定这都是由于指向PPTE的无效PTE导致的。事实上Copy On Write等等其他机制,均会发生Page Fault(《探寻Windows NT/2000 Copy On Write机制》有详细讨论)。但是正如我们前面提及的PPTE的bit 10为1,我们还是很容易的判定一个Page Fault是不是由于指向PPTE的无效PTE导致的。由于发生Page Fault的虚拟地址由CR2寄存器指定,经过几次尝试以后,我们继续以下的讨论:

    Break due to BPINT 0E   (ET=2.23 Seconds)
    :cpu

    Processor 00 Registers
    ----------------------
    CS:EIP=0008:801648A4  SS:ESP=0010:FCBEADC8
    EAX=C002100B  EBX=77E74A02  ECX=00000102  EDX=00000000
    ESI=00085108  EDI=000493E0  EBP=0140FF74  EFL=00000006
    DS=0023  ES=0023  FS=0038  GS=0000

    CR0=8000003B PE MP TS ET NE PG
    CR2=77D3BB26   //发生Page Fault的虚拟地址。
       .
       .
       .

    :page 77d3bb26
    Linear     Physical   Attributes
    77D3BB26   NP 01A714F6

    从PTE值01A714F6的bit 10为1我们知道这是一个指向PPTE的无效PTE。通过query命令我们可以找到CR2指定的地址,位于模块rpcrt4.dll中。从下面可以看到:

    :query 77d30000
    Context   Address Range      Flags     MMCI      PTE       Name
    explorer  77D20000-77D8E000  07100001  FF8D1328  E169C580  rpcrt4.dll

    结合我文章开始的介绍,通过以下的计算:

    :? (77d3bb26-77d20000)/1000*4+e169c580
    unsigned long = 0xE169C5EC, -513161748, "/xE1i/xC5/xEC"

    我们可以得到其实PTE 01A714F6应该指向0xE169C5EC位置。这时候由MMCI指向的Control Area,根据我上面提到的计算公式,即可以读出rpcrt4.dll偏移(0xE169C5EC-0XE169C580)/4*1000处,即0x1B000处的4K字节,读入虚拟地址77D3B000中((0xE169C5EC-0XE169C580)/4*1000+77D20000),而CR2指定的地址77D3BB26肯定在这4K之中。

    其实这样我们已经描述了MmAccessFault处理指向PPTE的无效PTE的一个典型过程。这里只是演示了原型PTE指向的页面未驻留在物理内存的情况,试想如果我们的页面已经在物理内存了,我们还有必要去费时的查找VAD吗?这就要涉及到无效的PTE如何定位原型PTE,所以我一直使用指向PPTE的无效PTE的叫法。《Inside Windows 2000》中指出指向PPTE的无效PTE的具体格式,但我发现其描述的不尽正确,我一直深信像作者那样能触及Windows 2000代码的人肯定不会有什么问题,所以我在理解PPTE时一直卡在此处。后来通过反汇编实现时发现实际上通过下面的方式来计算PPTE的位置:

   (PTE>>2) & 0x3FFFFE00 + (PTE & 0x000000FF) << 1 + 0xE1000000

    其中PTE为指向PPTE的无效PTE,0xE10000000是页交换区的起始地址。同样我们使用上面的例子来演示这个算法:

    上面的无效PTE为01A714F6,有了这个值,我们可以得到:

       PPTE Address = (0x01A714F6 >> 2) & 0x3FFFFE00 + (0x01A714F6 & 0x000000FF) << 1 + 0xE1000000
                    = 0x0069C53D & 0x3FFFFE00 + 0xF6 << 1 + 0xE1000000
                    = 0x69C400 + 0x1EC + 0xE1000000
                    = 0xE169C5EC

    与我们通过VAD查找到的PPTE位置0xE169C5EC一致。

    为了更好的理解PPTE,我们再来看一个例子。我们知道在Windows 2000/XP中ntdll.dll是个非常重要的dll,只要操作系统正常启动,ntdll肯定会被多个进程共享。我们用SoftICE作如下分析:

    :query -x 77f50000
    Context   Address Range      Flags     MMCI      PTE       Name
    smss      77F50000-77FF8000  07100005  80E6FA50  E131F9E8  ntdll.dll
        .
        .
        .
    explorer  77F50000-77FF8000  07100005  80E6FA50  E131F9E8  ntdll.dll
        .
        .
        .

    :addr smss
    :mod ntdll
    hMod Base     PEHeader Module Name      File Name
         77F50000 77F500E8 ntdll            /WINDOWS/system32/ntdll.dll

    根据ntdll的基地址77F50000,我们查看其硬件PTE:

    :dd 1df*1000+350*4+c0000000 l 4 //详细请参考《小议Windows NT/2000分页机制》
    0010:C01DFD40 02267027  02F2E005  02F2F005  00C7E4FA      'p&.............

    从smss进程的这些页表,我们很容易知道ntdll.dll第1至3个4K均驻留于物理内存地址中,因为它们都是有效的硬件PTE,而第四个PTE(00C7E4FA),虽然其是一个无效PTE(bit 0为0),但由于其是一个指向PPTE的PTE(bit 10为1),所以我们不能仅凭此PTE是个无效PTE,就断定ntdll.dll的第4个4K就不在物理内存中。我们要进一步的分析这个PTE,找出指向的PPTE判断这第4个4K是不是真的就是在磁盘中。OK,通过上面提及的算法,我们很容易的算出PPTE Address为E131F9F4,我们来看看这个PPTE的值:

    :dd e131f9f4 l 4 
    0010:E131F9F4 02F30121  02F31121  02F32121  02F33121      !...!...!!..!1..

    从值02F30121我们这时就可以判定这第4个4K也存在于物理地址中,位于Page Frame Number为02F30的物理内存中,剩下的就是查PFN Database了。

    我们也可以来查看查看explorer进程的ntdll.dll映射情况,来验证一下这种情况:

    :addr explorer
    :dd 1df*1000+350*4+c0000000 l 4
    0010:C01DFD40 02267025  02F2E025  02F2F025  02F30025      %p&.%...%...%...

    这回清楚了吧。文章开头我提及:“我们不需要更新所有引用这一页面的进程的硬件PTE,因为此时所有进程的PTE均指向PPTE,我们只要更新PPTE就能达到目的了”。从中我们也可以看到ntdll.dll的第4个4K实际上位于物理内存中,但Windows 2000/XP并没有更新每个引用此页面的PTE,就正如smss进程一样。而PPTE却已经指向其实际地址了。当smss进程首次访问这个区域时,内存管理器才将02F30025(假设属性与explorer进程使用这页的属性一样且为考虑访问位标志)这个有效的硬件PTE更新上面的00C7E4FA,现在一切都明朗了吧。

    本文虽然着重点在于介绍PPTE,但实际上我已将Section对象的内部机制说得非常清楚。这也是我原先将文章标题定为剖析Section之类的。关于PPTE,我的理解也经历了较多时间,主要是目前这部分资料实在是没有,仅有的《Inside Windows 2000》在没深入介绍的同时其指向PPTE的无效PTE格式未明确指出(特别是加上0xE1000000,这让我吃尽了苦头),本文介绍的这个格式我已经在Windows 2000及XP上测试过,实际上本文的两个例子一个是在Windows 2000 Server Build 2195,另一个在XP专业版Build 2600上演示的。

    在这次介绍PPTE后,我们来回顾一下内存管理器内部的几个千丝万缕的联系:

    FILEOBJECT的SECTION_OBJECT_POINTERS->DataSectionObject或SECTION_OBJECT_POINTERS->ImageSectionObject(决定于Section对象映射的文件的打开方式)指向Control Area,同时进程描述这文件映射的虚拟地址的VAD的MMCI成员(SoftICE叫法)也指向这个Control Area,Control Area底下存在一至多个SubSection,SubSection指向PPTE,PPTE table一般位于Control Area指向的Segment结构的底部。Section对象指向Segment;进程Page Table指向PPTE;这一切现在已描述的比较清楚了。还有一个主要的联系,即PFN Entry的Restore PTE(Original PTE)指向Subsection,这个关系我将在下次予以介绍。

    从《小议Windows NT/2000分页机制》到今天这篇介绍PPTE,我对Windows 2000/XP的内存管理部分才有了比较深入的理解,至于未提及到的Working Set等概念也是非常重要的。经历过很多的模糊,对内存管理器也总算有了些许概念了
内存相关内核知识点 传统的三级页表从上到下分为 PGD,PMD 和 PTE。后面引入了新的PUD( Page Upper Directory)。 PGD 依然保留为顶层的页表,同时将新级别插入在中间,即 PMD 级别的左边或者右边。基于该设计,最顶层都是 PGD,最底层也都是 PTE, ... 阅读详情

相关推荐

深入解析Windows操作系统 卷1(英文版·第7版) 上册高清完整版.pdf

深入解析Windows操作系统 卷1(英文版·第7版) 高清pdf版

[翻译]深入解析Windows操作系统(下)之第十章 内存管理

默认情况下,32位Windows上的进程的虚拟大小为2GB。如果映像被专门标记为大地址空间感知,并且系统通过一个特殊选项启动(稍后描述 在本章中),32位进程在32位Windows上可以增长到3GB,在64位Windows上可以增长到4GB。64位Windows上的进程虚拟地址空间大小为7,152 GB,IA64系统上的为8152GB 在x64系统上的2GB。(此值可以在未来的版本中增加。)正如您在第1部分的第2章“系统架构”中所看到的(特别是在表2-2中),目前Windows所支持的最大物理内存量从2GB

二次元怪兽的博客 4573

Pinocchio 安装教程|机器人学的必备库

安装pinocchio,不要安装pip3 install pinocchio!

darcsdn 1660

地址转译的相关问题(二)

地址转译的相关问题(二) 通过上一篇博客我们可以看到,地址的转译需要访问PTE(intel x86中PDE 也是按照下面的方式来解释的) 而PTE 又分好几种情况,比如有效的PTE,无效的PTE ,指向原型PTE 的无效PTE,有效的原型PTE,无效的原型PTE 等,下面我们来介绍相关概念 PTE结构: 位名称 含义 Accessed

商少 760

VS2019手动添加bits/stdc++.h全指南(附解决‘无法打开源文件‘错误)

本文详细介绍了在Visual Studio 2019中手动配置C++万能头文件bits/stdc++.h的完整方法,解决常见的'无法打开源文件'错误。通过分析GCC与MSVC编译器生态差异,提供了纯手动创建和MinGW移植两种可靠方案,并给出详细的步骤、常见错误排查及最佳实践建议,帮助开发者高效配置竞赛开发环境。

weixin_29011395的博客 547

GBASE南大通用GBase 8s数据库-从零开始的手动安装与实例配置指南

本文详细介绍了GBASE南大通用GBase 8s数据库的手动安装与实例配置全过程,包括环境准备、用户创建、安装包准备、实例创建、环境变量配置等关键步骤。通过实战经验分享和常见问题解决方案,帮助用户快速掌握GBase 8s数据库的安装与配置技巧,确保数据库稳定运行。

weixin_29209825的博客 454

windows内核 内存管理

一、几个基本的概念 1.存储器的金字塔结构 存储器从下之上依次是磁盘/flash、DRAM(内存)、L2-cache、L1-cache、寄存器,越在上面的存储器访问速度越快,同时价格也越昂贵,每一级都可以看做是下一级的缓存,内存是磁盘的缓存,cache是内存的缓存。 2.地址空间 地址空间就是一个非负正数的有序集合,如果是连续的即线性地址空间,从硬件的角度看就是处理器所能访问的存储器空间,...

weixin_30565199的博客 211

逆向工程实战:手把手教你破解药监局网站的动态数据加载机制(Python+Chrome开发者工具)

本文详细介绍了如何利用Python和Chrome开发者工具逆向破解药监局网站的动态数据加载机制。通过分析Ajax请求、解析JSON响应数据以及处理动态参数,帮助开发者高效采集动态加载内容,适用于爬虫开发与数据分析场景。

weixin_30832143的博客 67

原型PTE的定义和指向原型PTE的指针和mmvad->FirstPrototypePte的关系

原型PTE的定义和指向原型PTE的指针和mmvad->FirstPrototypePte的关系

linux-0.11调试教程 717

量子城域网建设设备系列(三):网络管理系统(NMS)

本文对量子网络中的网络管理系统进行了初步的介绍。

量子领域相关话题 2012

Raspberry Pi Pico SDK开发-Timer-定时

RP2040 由 Raspberry Pi 设计,具有双核 Arm Cortex-M0+ 处理器和 264KB 内部 RAM,并支持高达 16MB 的片外闪存。 广泛的灵活 I/O 选项包括 I2C、SPI 和独特的可编程 I/O (PIO)。 本实例将演示如何使用Timer的定时功能。

视觉与物联智能 2040

解析Winndows 2000/XP物理内存管理

解析Winndows 2000/XP物理内存管理   物理内存是相对比较紧张的资源,合理利用将是一个操作系统的性能的关键。Windows 2000/XP内部使用一个称为页框数据库(Page Frame Database)的结构用于描述物理内存的状态。本文将从这一结构入手详述Windows物理内存的组织与管理。    Windows将物理内存按PAGE_SIZE(在x

小发猫 1725

SpringBoot4+Java21前后端分离架构项目脚手架

本文介绍了基于SpringBoot 4和Java 21的前后端分离项目架构方案。主要内容包括:1)采用Spring Security 7实现JWT无状态认证和RBAC权限控制;2)后端使用MyBatis-Plus简化数据库操作,前端采用Vue3+Pinia组合;3)详细的项目结构设计,推荐按业务模块分包;4)核心依赖配置和架构设计图,涵盖认证授权流程。该方案结合最新技术栈,提供了生产级的全链路实现,特别适合需要快速构建现代化Web应用的项目。

Hello, New World! 421

[转]解析Winndows 2000/XP物理内存管理

物理内存是相对比较紧张的资源,合理利用将是一个操作系统的性能的关键。Windows2000/XP内部使用一个称为页框数据库(PageFrameDatabase)的结构用于描述物理内存的状态。本文将从这一结构入手详述Windows物理内存的组织与管理。Windows将物理内存按PAGE_SIZE(在x86上,为0x1000字节,即4K)为单位,将其划分,每一单元在页框数据库...

WiLL_2015的博客 253

YOLOv8【第十章:多任务扩展深度篇·第19节】YOLOv8 与 DeepLabV3+ 对比——实时性与精度的权衡!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。

**My Coding Family** 1万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

理解内存分页机制

在8086时期,寄存器是16位,无法存放20位的物理地址,为了解决这问题,人们提出了分段机制,分段机制就是将内存分段,每段大小64kb,在段寄存器中放入段基址,然后+上段偏移量就成了物理地址或者线性地址,为什么说是或呢?因为在采用分页基址的系统中,线性地址加上页偏移量才是真正的物理地址,如果没有用分页机制,计算出来的就是物理地址。windows NT引入了内存分页机制,此种内存管理

向往大海专栏 890

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Prototype PTE

#define MM_DEFAULT_PAGED_POOL_START (0xE1000000) #define MiPteToProto(lpte) (DWORD)((DWORD)(((((lpte)) >> 11) << 9) + \ (((((lpte))) > 23) + \ MM_DEFAULT_PAGED_POO

whowho的专栏 1417

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

EPROCESS 进程/线程优先级 句柄表 GDT LDT 页表 《寒江独钓》内核学习笔记(2)

在学习笔记(1)中,我们学习了IRP的数据结构的相关知识,接下来我们继续来学习内核中很重要的另一批数据结构: EPROCESS/KPROCESS/PEB。把它们放到一起是因为这三个数据结构及其外延和windows中进程的表示关系密切,我们在做进程隐藏和进程枚举的时候一定会涉及到这3类数据结构,所以这里有必要及时做一下总结,做个学习笔记,理清思路。   1. 相关阅读材料 《windows 内...

weixin_34323858的博客 894

None

VOID HookPage(PUCHAR Page) {   PHARDWARE_PTE_X86PAE PointerPte;   __try {     __asm {        mov   eax, Page        mov   eax, [eax]     }     PointerPte = (PHARDWARE_PTE_X86PAE)MiGetPteAddressPAE(Page);     if (PointerPte->Vali

callesp的专栏 557

分页机制-汇编代码分析

首先:在未开启分页机制的情况下,逻辑地址在分段管理机制作用下,直接转换成线性地址(物理地址);在开启分页机制的情况下,逻辑地址在分段管理机制下,转换成线性地址,然后在分页机制管理下,再从线性地址转换成物理地址。 然后给大家分享一下网上分页管理机制的示意图: 上图转换使用两级页表,第一级叫做页目录,大小为4KB,存储在一个物理页中,每个表项4字节长,所以共有1024 个表项(4KB/4B)。每个表项对应第二级的一个页表,第二级的每一个页表也有1024个表项,每一个表项对应一个物理页。 线性地址转换物理地址的

qq_43302174的博客 1087

透过MmIsAddressValid看Windows分页机制

标 题: 【原创】透过MmIsAddressValid看Windows分页机制作 者: combojiang时 间: 2008-03-16,00:53链 接: http://bbs.pediy.com/showthread.php?t=61327这两天在逆向分析MmIsAddressValid这个函数,学习了下PAE分页机制,并且也发现了一个问题。就是本机ntoskrnl中导出的MmIsAddre

求索 1960

内存管理(4)页面错误异常处理&物理内存管理&其他组件

这篇是Mm的最后一篇~18号po上来就一直设为私有状态,没时间修正... 忙得都是一些恶心事情,比如说恶心的物理实验。。就是在不知原理的情况下拿着些古怪的仪器去测出一堆即使自己瞎编也不会被发现的实现数据,然后老师签字,附带大篇幅手抄的预习报告和数据处理还有课后习题,一套流程下来需要10~15个小时,真装B啊~~~ 额,这学期开始的时候非常有兴趣将内核一些关键组件详细的研究一下,一直最搞

Returns' Station 3380

zpl指令打印中文内容_6.3 ECALL指令之前的状态

接下来,我将切换到gdb的世界中。 大家可以看我共享的屏幕,我们将要跟踪一个XV6的系统调用,也就是Shell将它的提示信息通过write系统调用走到操作系统再输出到console的过程。你们可以看到,用户代码sh.c初始了这一切。上图中选中的行,是一个write系统调用,它将“$ ”写入到文件描述符2。接下来我将打开gdb并启动XV6。作为用户代码的Shell调用write时,实际上调用的是关联...

weixin_39920629的博客 773

物理内存映射 获取PTE

IA-32的分页机制灰常给力的。弄几个函数玩typedef struct _HARDWARE_PTE_X86 { ULONG Valid : 1; ULONG Write : 1; ULONG Owner : 1; ULONG WriteThrough : 1; ULONG CacheDisable : 1; ULONG Accessed : 1; ULONG Dirty : 1; ULONG LargePage : 1;

xDragonx的专栏 4082

怎么把PPT转成EXE格式,详细步骤

<br />前几天,我做求婚记的PPT,把它转成了EXE格式。这样做,有以下几点好处:<br />1 方便没有安装PPT的人观看<br />2 可以保护PPT的内容不被COPY或者修改<br />3 便于播放,因为很多PPT里面使用了一些音频、视频资料。PPT里面使用的是相对路径,但是很多情况下,由于在COPY的时候不小心漏掉了资源文件或者COPY的路径不正确。从而导致制作的PPT不能够正确播放。<br /> <br />有朋友问到了我这个问题,怎么把PPT转换成EXE?<br />下面,我详细地给大家介绍

Stanley就是果果爹~ 1万+
上一篇: Win32程序函数调用时堆栈变化情况分析
下一篇: Visual Assist X 10.2(开发环境增强工具)
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值