大家都知道,已知虚拟地址然后取得其对应的物理地址很简单:paddr = GetPhyAddr(pvoid vaddr) // paddr = physical address
// vaddr = virtual address但现在要求直接输入一个物理地址,求其地址中的内容:pvalue = GetValueByPhyAddr(pvoid paddr) // paddr = physical address请大家畅所欲言,只要能达到目的,用什么编程方法都可以,包括用
驱动切进RING0,转贴都可以。但是有一个要求,就是要把大致编程思路说一下 :)
纯粹探讨,也不着急,大家边喝可乐边聊...
// vaddr = virtual address但现在要求直接输入一个物理地址,求其地址中的内容:pvalue = GetValueByPhyAddr(pvoid paddr) // paddr = physical address请大家畅所欲言,只要能达到目的,用什么编程方法都可以,包括用
驱动切进RING0,转贴都可以。但是有一个要求,就是要把大致编程思路说一下 :)
纯粹探讨,也不着急,大家边喝可乐边聊...
解决方案 »
- vc读EXCEL任意给定一个值怎么获取该值所在的列
- 关于划线的一个奇怪问题
- 用ODBC方法把大数组存入数据库
- 用软按键映射键盘按键怎么实现按下一直按着软件键时,连续写键值的操作?
- CListBox問題
- 继承自列表框类CListCtrl的类CMyListCtrl重载了函数OnNotify 代码如下,其实现的功能为何
- 在类使用用线程函数
- 请问一下那个高手可以告诉我一下,应该怎么做才可以让程序支持中文路径,中文菜单阿?
- 求救:如何在按钮处理函数中获得该按钮的ID号?
- 谁能帮帮我!急死了!我真的不懂!来者有分!
- 开发的一个类CAD程序,在平板电脑(带手写功能)上使用时,用笔操作时经常死机,而用普通鼠标操作时则一切正常,谁能指点一下
- 考试通过啦,散100分庆祝,呵呵
===================================================
如果是在Win32平台下, 是求虚拟地址的内容?http://support.microsoft.com/kb/189327/
http://support.microsoft.com/?scid=kb%3Ben-us%3B189327&x=9&y=10
别的方法就是碰运气了,如果要访问的物理内存就是自己进程中用的,那么GetPhyAddr就可以搜索到的,X86中可以以4K为单位进行搜索,搜不到就只能MmMapIoSpace
做过单片机和ARM的程序,呵呵,实现起来easy
但是windows下面不知道有没有提供这方面的操作权限那?
随便弄一个Driver就OK
ZwOpenSection后ZwMapViewOfSection即可void OpenPhysicalMemory()
{
UNICODE_STRING Name;
WCHAR memName[] = L"\\Device\\PhysicalMemory";
OBJECT_ATTRIBUTES ObjectAttributes; RtlInitUnicodeString(&Name, memName);
InitializeObjectAttributes(&ObjectAttributes, &Name,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL, NULL );
ZwOpenSection( &hPMem, SECTION_MAP_READ | SECTION_MAP_WRITE, &ObjectAttributes );
}
...
1.进RING0,以下是我读硬盘系列号的部分程序:
void _stdcall ReadIdeSerialNumber()
{
_asm
{
push eax
//获取修改的中断的中断描述符(中断门)地址
sidt IDTR
mov eax,dword ptr [IDTR+02h]
add eax,3*08h+04h
cli
//保存原先的中断入口地址
push ecx
mov ecx,dword ptr [eax]
mov cx,word ptr [eax-04h]
mov dword ptr OldInterruptAddress,ecx
pop ecx
//设置修改的中断入口地址为新的中断处理程序入口地址
push ebx
lea ebx,InterruptProcess
mov word ptr [eax-04h],bx
shr ebx,10h
mov word ptr [eax+02h],bx
pop ebx
//执行中断,转到Ring 0(类似CIH病毒原理)
int 3h
//恢复原先的中断入口地址
push ecx
mov ecx,dword ptr OldInterruptAddress
mov word ptr [eax-04h],cx
shr ecx,10h
mov word ptr [eax+02h],cx
pop ecx
sti
pop eax
}
}
2.用ntdll的ZWopenS等,这是我用BCB写的一个类:
PhyMemMap::PhyMemMap(DWORD StartAddr,DWORD Length)
{
switch(Win32Platform)
{
case VER_PLATFORM_WIN32_NT:
PhyMemAddr.LowPart=StartAddr;
PhyMemAddr.HighPart=0x0;
ssize=Length;
BaseAddr=NULL;
struniph.Buffer=L"\\device\\physicalmemory";
struniph.Length=0x2c;
struniph.MaximumLength =0x2e;
obj_ar.Attributes =64;
obj_ar.Length =24;
obj_ar.ObjectName=&struniph;
obj_ar.RootDirectory=0;
obj_ar.SecurityDescriptor=0;
obj_ar.SecurityQualityOfService =0; hinstLib = LoadLibrary("ntdll.dll");
if (hinstLib==NULL)
{
// ShowMessage("Error:Can't open ntdll.dll");
return;
}
ZWopenS=(ZWOS)GetProcAddress(hinstLib,"ZwOpenSection");
ZWmapV=(ZWMV)GetProcAddress(hinstLib,"ZwMapViewOfSection");
ZWunmapV=(ZWUMV)GetProcAddress(hinstLib,"ZwUnmapViewOfSection");
if(ZWopenS==NULL || ZWmapV==NULL || ZWunmapV==NULL)
{
// ShowMessage("Error:Can't get function address ");
return;
}
ZWopenS(&hSection,4,&obj_ar);
if(hSection==NULL)
{
// ShowMessage("Error:Can't open Zw ");
return;
}
ZWmapV(
(HANDLE)hSection,
(HANDLE)0xffffffff,
&BaseAddr,
0,
ssize,
&PhyMemAddr,
&ssize,
1,
0,
2
);
if(BaseAddr==NULL)
{
// ShowMessage("Error:Can't Map Adderess");
return;
}
break;
case VER_PLATFORM_WIN32_WINDOWS:
ssize=Length;
BaseAddr= new unsigned char[ssize];
try
{
memcpy(BaseAddr,(char *)(StartAddr),ssize);
}
catch(...)
{
// ShowMessage("Can't copy memory");
delete BaseAddr;
BaseAddr=NULL;
}
break;
default:
break; }
}
PhyMemMap::~PhyMemMap()
{
switch(Win32Platform)
{
case VER_PLATFORM_WIN32_NT:
if (hSection!=NULL)
ZWunmapV((HANDLE)hSection,&BaseAddr);
if (hinstLib!=NULL)
FreeLibrary(hinstLib);
break;
case VER_PLATFORM_WIN32_WINDOWS:
if(BaseAddr!=NULL)
delete BaseAddr;
break;
}
}
bool PhyMemMap::Read(unsigned char *buff,unsigned startaddr,unsigned rsize)
{
if (BaseAddr==NULL || startaddr>=ssize)
return false;
if (startaddr+rsize>=ssize)
rsize=ssize-startaddr-1;
memcpy(buff,BaseAddr+startaddr,rsize) ;
return true;
}
3.用第三方模块(如winio等),这是最简单的方法
作为软件开发人员,多数对保护模式都感到神秘和不易理解。本人在开发32位微内核抢占式多线程操作系
统过程中,深入了解到CPU 的地址机理,在这里将分析CPU 的工作原理,解开保护模式的神秘面纱,读者
将会发现保护模式其实与实模式一样简单和易于控制。在此基础上用四五十行C 语言程序做到进出保护模
式和在实模式之下直接访问整个4GB内存空间。
虽然有许多书籍对保护模式作解释,但没有一本能简单明了地解释清楚,冗长烦杂的术语让人看着想打瞌
睡,甚至还有许多用汇编写的(可能根本不能运行的)保护模式试验程序,事实上用C 语言本身就可以做
保护模式的进出工作。
我们可能知道CPU 上电后是从ROM 中的BIOS 开始运行,而Intel 的文档却说80x86 CUP 上电总是从最高
内存下16字节开始执行,那么BIOS是处在内存的最顶端64K (FFFF0000H)还是1M之下的64K(F0000H)处
呢?事实上在这两个地方都同时出现(可用后面存取4GB 内存的程序验证)。
为什么?为了弄清楚以上问题,首先要了解CPU 是如何处理物理地址的?真的是在实模式下用段寄存器左
移4 位与偏移量相加,或在保护模式下用段描述符中的基地址加偏移量?两者毫无关联吗?答案是两者其
实是一样的。当Intel推出80286时其寻址空间变成了24位,从8086的20位到24位,十分自然地要加大段寄
存器才行,实际上它们都被加大了,只是由于保护的原因加大的部分没有被程序员看见,到了80386之后
地址又从24位加大到32位(80386SX是24位)。
在8086时CPU 只有“看得见部分”,从而它直接参与了地址的形成运算,但在80286 之后,在“看不见部
分”中已经包含了地址值,“看得见部分”就退化为只是一个标号再也不用参与地址形成运算了。地址的
形成总是从“不可看见部分”取出基址值与偏移相加形成地址。也就是说在实模式下当一个段寄存器被装
入一个值时,“看不见部分”的界限被设成FFFFH,基址部分才是要装入值左移4位,属性部分设成16位0
特权级。这个过程与保护模式时装入一个段存器是同理的,只是保护模式的“不可见部分”是从描述表中
取值,而实模式是一套固定的过程。
对于CPU在形成地址时,是没有实模式与保护模式之分的,它只管用基址(“不可见部分”)去加上偏移
量。实模式与保护模式的差别实际上只是在保护处理部件是否工作得更精确而已,比如不允许代码段的写
入等。在实模式下的段寄存装入有固定的形成办法从而也就不需要保护模式的“描述符”了,因此保持了
与8086/8088的兼容性。而“描述符”也只是为了装入段寄存器的“不可见部分”而设的。
从上面的“整个段寄存器”可见CPU的地址形成与“看得见部分”的当前值毫无关系,这也解释了为什么
在刚进入保护模式时后面的代码依然被正确地运行而这时代码段寄存器CS的值却还是进入保护模式前的
实模式值,或者从保护模式回到实模式时代码段CS被改变之前程序是正常地工作,而不会“突变”到CS左
移4位的地址上去,比如在保护模式时CS是08H的选择器,到了实模式时CS还是08H,而地址不会突然变成
80H加到偏段量中去。因为地址的形成不理会段寄存器(“看得见部分”)的当前值,这一个值只是在被
装入时对CPU有用。地址的形成与CPU的工作模式无关,也就是说实模式与0特权级保护模式不分页时是一
模一样的。明白了这一机理,在实模式下一样可以处理通常被认为只有在保护模式才能做的事,比如访问
整个机器的内存。可以不必理会保护模式下的众多术语,或者更易于理解,如选择寄存器就是“看得见部
分”,描述符是为了装入“不可见部分”而设的。
作为验证CPU的这种机理,这里写了一个实模式下访问4GB内存的C程序。有一些书籍也介绍有同样功能的
汇编程序,但它们都错误地认为是利用80386芯片的设计疏漏。实际上Intel本身就在使用这种办法,使得
CPU上电时能从FFFFFFF0H处开始第一条指令,这种技术在286之后的每一台机器每一次冷启动时都使用,
只是我们不知道罢了。
EIP=0000FFF0H
这样CS∶EIP形成了FFFFFFF0H的物理地址,当CPU进行一次远跳转重新装入CS时基址就变了。为了访问4G
内存空间,必须有一个段寄存器的“不可见部分”的界限为4G-1,基址为0,这样就包含了4GB内存,不必
理会可见部分的值。显然要让段寄存器在实模式下直接装入这些值是不可能的。唯一的办法是让CPU 进入
一会儿保护模式在装入了段寄存器之后马上回到实模式。
进入保护模式十分简单,只要建好GDT把CRO寄存器的位0置上1,CPU 就在保护模式了,从前面所分析CPU
地址形成机理可知,这时不必理会寄存器的“看得见部分”值是否合法,各种段寄存器是一样可用的,
就像没进保护模式一样。在把一个包含有4GB地址空间的值装入某个段寄存器之后就可返回实模式。
预先可建好GDT如下:
unsigned long GDT-Table[]={0,0, //空描述符,必须为零
0x0000FFFF,0xCF9A00, //32位平面式代码段
0x0000FFFF,0xCF9200 //32位平面式数据段
};
只是为了访问数据的话只要2个GDT就足够了,因为并没有重装代码段,这里给出3个GDT只是为了完整性。
回实模式后恢复IDTR并开中断。另外A20地址线的控制对于正确访问整个内存也很重要,在进入保护模式
前要让8042打开A20地址线。在这个例子里FS段寄存器设成可访问4GB内存的基址和界限,由于在DOS中很
少有程序会用到GS、FS这两个386增加的段寄存器,当要读写4GB范围中的任一个地方时都可通过FS段来达
到,直到FS在实模式下被重装入冲掉为止。
这个例子在386SX、386DX、486 上都运行通过。例子里加有十分详细的注释,由于这一程序是用BC 3.1
编译连接的,而其连接器不能为DOS程序处理32位寄存器,所以直接在代码中加入操作码前缀0x66和地址
前缀0x67,以便让DOS实模式下的16位程序可用32位寄存器和地址。程序的右边以注释形式给出等效的32
位指令。
要注意16位的指令中mov al, byte ptr [BX]的指令码正好是32位的指令mov al, byte ptr[EDI]。读者
可用这个程序验证BIOS是否同时在两个区域出现。如果有线性定址能力的VESA显示卡(如TVGA9440)还可
进一步验证线性显示缓冲区在1MB之上的工作情况。
#include <dos.h>
#include <stdio.h>
unsigned long GDT-Table[]= { 0, 0 //NULL - 00H
0x0000FFFF,0x00CF9A00,
0x0000FFFF,0x00CF9200
};
unsigned char OldIDT [6]={0};
unsigned char pdescr-tmp [6]={0};
#define KeyWait() {while(inportb(0x64) &2);}
void A20Enable(void)
{
KeyWait();
outportb(0x64,0xD1);
KeyWait();
outportb(0x60,0xDF);
KeyWait();
outportb(0x64,0xFF);
KeyWait();
}
void LoadFSLimit4G(void)
{
A20Enable();
asm
{
cli // Disable inerrupts
sidt OldIDT // Save OLD IDTR
lidt pdescr-tmp // Set up empty IDT. Disable any interrupts
}
asm
{
DB 0x66
mov cx, ds
DB 0x66
shl cx, 4
mov word ptr pdescr-tmp[0],(3*8-1)
DB 0x66
xor ax, ax
mov ax, offset GDT-Table
DB 0x66
add ax, cx
mov word ptr pdescr-tmp [2], ax
DB 0x66
shr ax, 16
mov word ptr pdescr-tmp [4], ax
lgdt pdescr-tmp
}
asm
{
mov dx, 0x10 // The Data32 Selector
DB 0x66, 0x0f, 0x20, 0xc0 // mov eax, cr0
DB 0x66
mov bx, ax
or ax, 1
DB 0x66, 0x0f, 0x22, 0xc0
jmp flush
} // Clear machine perform cache.
flush: // Now In Flat Mode, But The CS is Real Mode Value.
asm
{
DB 0x66
mov ax, bx
DB 0x8e, 0xe2 // mov fs, dx
DB 0x66, 0x0f, 0x22, 0xc0 // mov cr0, eax
lidt OldIDT
sti
}
}
unsigned char ReadByte (unsigned long Address)
{
asm db 0x66
asm mov di,word ptr Address // MOV EDI, Address
asm db 0x67 //32 bit Address Prefix
asm db 0x64 //FS:
asm mov al,byte ptr [BX] // =MOV AL, FS: [EDI]
return -AL;
}
unsigned char WriteByte(unsigned Long Address)
{
asm db 0x66
asm mov di,word ptr Address //MOV EDI, Address
asm db 0x67 //32 bit Address Prefix
asm db 0x64 //FS:
asm mov byte ptr [BX],al //=MOV FS: [EDI],AL
return -AL;
}
//////// Don t Touch Above Code ///
void Dump4G(unsigned long Address)
{
int i;
int j;
for(i=0; i<20; i++)
{
printf("%081X: ", (Address+i*16));
for(j=0; j<16; j++)
printf("% 02X", ReadByte(Address+i*16+j));
printf(" ");
for(j=0; j<16; j++)
{
if(ReadByte(Address+i*16+j) < 0x20)
printf(" . ");
else
printf(" %C ", ReadByte(Address+i*16+j));
}
printf("\n");
}
}
int main()
{
unsigned long Address=0;
unsigned long tmp;
LoadFSLimit4G();
printf("====Designed By Southern. 1995.7.17====\n");
printf (" Now you can Access The Machine All 4G Memory.\n");
printf (" Input the Start Memory Physical to DUMP. \n");
printf (" Press D to Cuntinue DUMP, 0 to End & Quit, \n");
do
{
printf("-");
scanf("%IX", &tmp);
if(tmp==0x0d)
Address+=(20*16);
else
Address=tmp;
Dump4G(Address);
}while(Address != 0);
return 0;
}
2 ZwOpenSection 打开物理内存对象,然后映射
3 第三方模块(如winio等)(to keiy() :你的第一种方法没看懂,可否详细讲讲)暂时这3种方法和我的方法都不相同,再看看还有没有别的方法,
然后公布我的方法。:)
void _stdcall ReadIdeSerialNumber()
{
...
} 是不是在98下运行的,另外看程序好像是中断处理,和硬盘序号无关,另外IDTR,OldInerrupt d等变量或函数未定义,能写一个全的吗
------
这种方法我持保留态度, 即使是ring0也不可能通过汇编直接访问物理内存, 除非你暂时禁止分页. ring0是说你可以访问物理内存,不是说你能直接访问内存.
-------
这种方法我以前试过(试着dump smbios table), 但是不是每次都能成功(有些机器上能够成功,有些不能), 具体原因不详.