Windows 95/98下直接访问物理内存
在很多情况下,我们都有直接访问物理内存的要求,如在实时高速数据采集系统中,
对I/O板上配置的存储器的访问。但是,为了保证系统的安全性和稳定性,操作系统
并不提倡应用程序直接访问硬件资源, 因此,随着操作系统的进步,导致了目前存在
的这样一个不幸的事实: 以前在DOS下很容易实现的特定物理内存的读写操作,在Windows
下却变得相当困难。
本文主要讨论如何在Windows 95/98下实现物理内存的直接读写操作。为了论述清
楚这个问题,有必要叙述保护模式的寻址方式以及W indows 95/98的内存管理方式。
Windows 95/98内存管理方式
Windows 95/98工作在32位保护模式下,保护模式与实模式的根本区别在于CPU寻址方
式上的不同:尽管两者对应的内存地址均为"段地址:偏移量"形式,但在保护模式下,
"段地址"代表的值已不再是实模式中段的起始基准地址了;对于CS、DS、ES、SS寄存
器,在实模式下,这些寄存器的值左移4位,再加上偏移量,即得到物理地址,而在保护
模式下,这些寄存器的值为"段选择符",它实际上是一个查全局描述符表(G DT)或局
部描述符表(LDT)的索引,据此在GDT或LDT找到对应的段描述符,从而获得段的基址及
类型等信息,再根据偏移量,才能得到线性地址。如果操作系统没有采用分页机制,
那么得到的线性地址即为物理地址,否则,线性地址需要进一步经过分页机制才能得
到物理地址。这就是保护模式下的"段页式寻址机制"。
Windows 95/98使用4GB的虚拟内存地址空间,应用程序访问内存使用虚拟地址,从虚
拟地址到物理地址的转换过程如图1所示: 图1 虚拟地址到物理地址的转化过程
对于图1中的分页机制,Windows 95/98采用两级页表结构,如图2 所示。图2 采用的
分页机制的两级页表结构
从图2可知,线性地址被分割成页目录条目(PDE)、页表条目(PTE) 、页偏移地址(Off set)
三个部分。当建立一个新的WIN 32进程时,Wi ndows 95/98会为它分配一块内存,并
建立它自己的页目录、页表,页目录的地址也同时放入进程的现场信息中。当计算一
个地址时,系统首先从控制寄存器CR3中读出页目录所在的地址(该地址为物理地址,
并且是页对齐的),然后根据PDE得到页表所在的地址,再根据PTE得到包含了实际Code
或Data的页帧, 最后根据Offset访问页帧中的特定单元。
常用内存段的段选择符
从上述所介绍的Windows 95/98采用的分段、分页机制可看出,要想在Windows 95/98
下直接访问物理内存,关键是得到欲访问物理内存所在的内存区域对应的段选择符。
一般说来,要求直接访问的物理内存都与实模式下能够寻址的内存有关(即DOS能直
接访问的1M物理内存)。在Windows 3.X中,Microso ft给出了DOS常用段的段选择符,
如_000 0H(未公开),_B800H,_F000H( 已公开),等等,均可以在KERNEL中找到,应用
程序可以直接使用这些段选择符,实现物理内存的直接访问。而在Windows 95/98中,
Microsoft 却不在任何文档中提供这些段的预定义,在KERNEL中也不提供相应的段选
择符。但是,Windows 95 /98确实给DOS下的这些常用内存段定义了相应的段描述符。
通过SoftIce 3.02 for Win dows 95/98,我们得到了关于LDT的如下信息:
...... :ldt
LDTbase=80003000 Limit=3FFF
……
1007 Data16 00000C90 0000FFFF 3 P RW
100F Data16 00000000 0000FFFF 3 P RW
1017 Data16 00000400 0000FFFF 3 P RW
101F Data16 000F0000 0000FFFF 3 P RW
1027 Data16 000A0000 0000FFFF 3 P RW
102F Data16 000B0000 0000FFFF 3 P RW
1037 Data16 000B8000 0000FFFF 3 P RW
103F Data16 000C0000 0000FFFF 3 P RW
1047 Data16 000D0000 0000FFFF 3 P RW
104F Data16 000E0000 0000FFFF 3 P RW
……
其中,每一行对应一个段描述符,第一栏为其段选择符,第二栏为段描述符的类型,
第三栏为段的基地址(线性地址),第四栏为段的限长 ,第五栏为段描述符的特权级,
第六栏标志对应段是否存在于内存中, 第七栏表示段的访问权限。
可以看出,这些段的基地址与DOS下的常用内存段完全吻合,并且均为16位的数据段,
限长为64K(0XFFFF),供应用程序访问,都存在于内存中,可读写。实践证明,这些段
就是D OS的常用内存段,也就是说, 这里的线性地址即为物理地址。因此,可以用这
些段选择符对相应的物理内存进行访问。
从程序运行的健壮性考虑,不应该直接应用上述段选择符,而应该用GetThreadSelec
torEntry()函数得到欲访问物理内存对应的段选择符,该API函数的原型定义为
BOOL GetThreadSelectorEntry (
HANDLE hThread,
// handle of thread that contains selector
DWORD dwSelector,
// number of selector value to look up
LPLDT_ENTRY lpSelectorEntry
// address of selector entry structure
);
其中,LDT_ENTRY的结构定义如下
typedef struct _LDT_ENTRY { // ldte
WORD LimitLow;
WORD BaseLow;
union {
struct {
BYTE BaseMid;
BYTE Flags1;
BYTE Flags2;
BYTE BaseHi;
} Bytes;
struct {
DWORD BaseMid : 8;
DWORD Type : 5;
DWORD Dpl : 2;
DWORD Pres : 1;
DWORD LimitHi : 4;
DWORD Sys : 1;
DWORD Reserved_0 : 1;
DWORD Default_Big : 1;
DWORD Granularity : 1;
DWORD BaseHi : 8;
} Bits;
} HighWord;
} LDT_ENTRY, *PLDT_ENTRY;
用下面的代码可以得到基地址为BASE_DESIRED,限长为0XFFFF的
内存段对应的段选择符:
......
extern CLDTApp theApp;
WORD wSelector; // 内存段对应的段选择符
LDT_ENTRY ldtEntry;
DWORD base, baseMid, baseHigh;
DWORD limit, limitHigh;
for ( WORD sel = 7; sel <= 0xffff; sel +=8 ) {
if (::GetThreadSelectorEntry ( theApp.m_hThread,
DWORD ( sel ), &ldtEntry ) ) {
baseMid = ldtEntry . HighWord . Bytes . BaseMid;
baseMid <<= 16;
baseHigh = ldtEntry . HighWord . Bytes . BaseHi;
baseHigh <<= 24;
base = ldtEntry . BaseLow + baseMid +
baseHigh;
limitHigh = m_ldtEntry . HighWord . Bits . LimitHi;
limitHigh <<= 24;
limit = limitHigh + m_ldtEntry . LimitLow;
if ( 0xFFFF == limit )
if ( BASE_DESIRED == base ) {
// BASE_DESIRED为内存段对应的基地址
wSelector = sel;
break; }}}
直接访问物理内存的实现
得到了段选择符之后,即可把该段选择符置于相应的段寄存器中( 不能用CS,DS),
用该寄存器进行数据访问。需注意的是,任何非法段选择符写入段寄存器将会导
致通用保护错误(General Protection Faul t)。
下面的代码实现物理内存的读/写操作(段选择符用上述方法得到):
void WriteMemory(WORD sel, DWORD dwOffset, const char * str, UINT length)
{
char cWrite;
for ( UINT i = 0; i < length; i ++ )
{
cWrite = str [i];
_asm { push es mov ax, sel mov es, ax
mov ebx, dwOffset mov al, cWrite mov byte ptr es:[ebx], al
inc dwOffset pop es
}
}
}
void ReadMemory ( WORD sel, DWORD dwOffset,char * str, UINT length )
{
char cRead;
for ( UINT i = 0; i < length; i ++ ) {
_asm {
push es
mov ax, sel
mov es, ax
mov ebx, dwOffset
mov al, byte ptres:[ebx]
mov cRead, al
inc dwOffset
pop es
}
str [i] = cRead;
}
}
本文所用操作系统为中文Windows 95 OSR 2.0以及中文Windows
98,编程环境为Vis ual C++ 5.0。
相关推荐
C++实现FTP和HTTP下载通讯:断点续传技术解析
在当今的信息技术领域,网络协议是支撑起互联网通信基础架构的核心组件。其中,FTP(File Transfer Protocol)和HTTP(Hypertext Transfer Protocol)是两种非常重要的应用层协议。它们被广泛用于文件的上传和下载,以及网页的访问和数据传输。
Windows获取物理内存的2种方式 - 随笔记录
1 typedef enum _SYSTEM_INFORMATION_CLASS { 2 SystemBasicInformation, 3 SystemProcessorInformation, // obsolete...delete 4 SystemPerformanceInformation, 5 Sy...
HTTP与FTP多线程下载实现技术详解
HTTP(HyperText Transfer Protocol)是用于从万维网(WWW)服务器传输超文本到本地浏览器的传输协议,属于应用层协议。其采用请求-响应模型,客户端(如浏览器)向服务器发送请求,服务器响应并返回数据。HTTP协议基于TCP/IP协议栈,通常使用端口80(HTTP)或443(HTTPS)进行通信。本章详细讲解了多线程下载完成后数据合并与顺序保持的核心方法。
32位/64位WINDOWS驱动之物理地址读写内存方法4
自写函数 通过PID切换到目标进程环境 计算出物理地址把物理地址映射到当前进程 不使用时需要用ZwUnmapViewOfSection取消映射//转换成 物理地址的方式 读取进程虚拟地址。
Windows CE下访问物理内存的一些方法!!
嵌入式设备与桌面PC的一个显著不同是它的应用程序中通常需要直接访问某一段物理内存,这在驱动程序中对物理内存的访问尤为重要,尤其是像ARM体系结构下,I/O端口也被映射成某一个物理内存地址。因此,与桌面版本Windows相比,Windows CE提供了相对简单的物理内存访问方式。无论是驱动程序还是应用程序都可以通过API访问某一段物理内存。Windows CE的有些函数中需要用到物理内存结构
Android多任务并行下载、断点续传
这个开源库叫 Aria,是我在搞断点续传时发现的,仔细了解后发现,真香!!! 它是一个简单易用,稳当高效的下载框架,不仅可以断点续传,还支持多任务自动调度,它更加灵活的事件监听器,让你能更容易的监听暂停、恢复、完成、失败、取消下载等状态。 开源库地址是:https://github.com/AriaLyy/Aria 目前已经有3.2k star,Aria有以下特点: 简单、方便 可以在Acti...
FTP/HTTP 多线程断点续传下载
支持FTP和HTTP协议的多线程断点续传下载程序,使用C++实现,支持Windows和Linux平台。
Http/FTP多线程断点续传下载组件
在做文件下载的过程中,找到一个不错的Http/FTP多线程断点续传下载DLL,感觉还不错,挺稳定的,至现在为止还未遇到用它导致崩溃的问题;功能也比较全 支持多线程,断点续传,显示详细下载过程,自动查找镜像网址,支持代理传输等 调用Http/Ftp的dll 的部分代码接口,如下 // 启动HttpFtp内核 HttpFt...
windows驱动开发-内存概述
90%的程序问题都是由内存引起的,剩下的10%是使用内存引起的!”这是一句非常经典的论证,实际上,在程序开发中,内存问题就是最大的问题,没有之一。现代的计算机体系中,内存承载了太多的功能,内存既是数据的载体,也是指令的载体,甚至还因为虚拟内存技术和磁盘挂钩,故上面那句话的意思是,所有的程序问题都可以从内存方面找到一部分原因。注意: 下面的讨论中,我们使用windows 10 x64版本来讨论64位地址空间下的内存话题。
Win64 驱动内核编程-27.强制读写受保护的内存
强制读写受保护的内存 某些时候我们需要读写别的进程的内存,某些时候别的进程已经对自己的内存读写做了保护,这里说四个思路(两个R3的,两个R0的)。 方案1(R3):直接修改别人内存 最基本的也最简单的就是直接通过WriteProcessMemory 和 ReadProcessMemory对没有进行保护的程序的内存进行修改,一些单机游戏辅助什么的可能会有这种简单方式修改其他进程内存。 方案2(R3...
翻译 《The Old New Thing》 - Windows 95 doesn’t boot with more than 1GB of RAM
p=42903Raymond Chen 2003年8月14日。
WINDOWS核心编程——Windows内存管理
想要了解Windows内存体系结构首先要对系统的内存的分段分页和进程隔离机制要有所了解。系统为了对进程进行隔离,使得每个进程只能访问自己申请的内存而不能访问其他进程的内存资源,对每个进程的内存使用线性地址编制,在通过内存的分页机制在进程需要访问物理内存时通过进程的页表找到世界的物理内存的地址通过系统读写内存中的数据。在早期总线(20位寻址1M)大于寄存器(16位寻址64k)的情况下为了表示更多的物
Windows内存管理
本文主要内容: 1.基本概念:物理内存、虚拟内存;物理地址、虚拟地址、逻辑地址;页目录,页表 2.Windows内存管理 3.CPU段式内存管理 4.CPU页式内存管理 一、基本概念 1. 两个内存概念 物理内存:人尽皆知,就是插在主板上的内存条。他是固定的,内存条的容量多大,物理内存就有多大(集成显卡系统除外)。但是如果程序运行很多或者程序本身很大的话,就会导致大量的物理内存
各种操作系统下的容量界限
因为硬盘使用的最高逻辑层就是操作系统,我们还要看看各种操作系统下的容量界限。即使操作系统支持大于8.4GB的硬盘容量,整个电脑系统也有可能不认全部的硬盘。这时需要使用分区软件如MaxBlast,或购买支持扩展INT13的升级卡。另外,有些操作系统还有分区大小的限制,这时需要创建多个分区以达到硬盘的全部使用。 1、DOS6.22或更低的版本:不支持大于8.4GB的硬盘,目前无解决办法。 2、Wi
windows 物理内存获取
由于我一般使用的虚拟内存, 有时我们需要获取到物理内存中的数据(也就是内存条中的真实数据), 按理说是很简单,打开物理内存,读取就可以了.但似乎没这么简单: [cpp] view plain copy print? #include"windows.h" //定义相应的变量类型,见ntddk.h typedefLONG...
1253




被折叠的 条评论
为什么被折叠?



