大家都知道,已知虚拟地址然后取得其对应的物理地址很简单:paddr = GetPhyAddr(pvoid vaddr) // paddr = physical address
                                // vaddr = virtual address但现在要求直接输入一个物理地址,求其地址中的内容:pvalue = GetValueByPhyAddr(pvoid paddr)   // paddr = physical address请大家畅所欲言,只要能达到目的,用什么编程方法都可以,包括用
驱动切进RING0,转贴都可以。但是有一个要求,就是要把大致编程思路说一下 :)
纯粹探讨,也不着急,大家边喝可乐边聊...

解决方案 »

  1.   

    WinIO程序库允许在32位的Windows应用程序中直接对I/O端口和物理内存进行存取操作google winio.dll
      

  2.   

    用MmMapIoSpace转到虚拟地址,然后再读
      

  3.   

    但现在要求直接输入一个物理地址,求其地址中的内容:
    ===================================================
    如果是在Win32平台下, 是求虚拟地址的内容?http://support.microsoft.com/kb/189327/
    http://support.microsoft.com/?scid=kb%3Ben-us%3B189327&x=9&y=10
      

  4.   

    标准方法就是MmMapIoSpace,X86访问物理内存都是通过内存管理单元的页面隐射完成的
    别的方法就是碰运气了,如果要访问的物理内存就是自己进程中用的,那么GetPhyAddr就可以搜索到的,X86中可以以4K为单位进行搜索,搜不到就只能MmMapIoSpace
      

  5.   

    还真的没有想过在windows下直接操作物理地址
    做过单片机和ARM的程序,呵呵,实现起来easy
    但是windows下面不知道有没有提供这方面的操作权限那?
      

  6.   

    一个物理地址可能不止映射到一个虚拟地址~操作物理RAM其实很容易的
    随便弄一个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 );
    }
    ...
      

  7.   

    我觉行有三种方法可以:
    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等),这是最简单的方法
      

  8.   

    (转)在DOS实模式下直接存取4GB内存   
        
      作为软件开发人员,多数对保护模式都感到神秘和不易理解。本人在开发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只是为了完整性。   
        
      

  9.   

    通常在进入保护模式时要关闭所有的中断,把IDTR的界限设置为0,CPU自动关闭所有中断,包括NMI,返   
      回实模式后恢复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;   
      }
      

  10.   

    一直在潜水观望中~到现在为止已大致有3种方法:1 MmMapIoSpace
    2 ZwOpenSection 打开物理内存对象,然后映射
    3 第三方模块(如winio等)(to keiy() :你的第一种方法没看懂,可否详细讲讲)暂时这3种方法和我的方法都不相同,再看看还有没有别的方法,
    然后公布我的方法。:)
      

  11.   

    我的第一种方法是进CPU RING0的方法,在CPU RING0级,应该可以直接对物理内存访问的
      

  12.   

    to:keiy() 1.进RING0,以下是我读硬盘系列号的部分程序:
    void _stdcall ReadIdeSerialNumber()
    {  
    ...
    }  是不是在98下运行的,另外看程序好像是中断处理,和硬盘序号无关,另外IDTR,OldInerrupt d等变量或函数未定义,能写一个全的吗 
      

  13.   

    我用delphi,一般都是那来主义:这家公司的io控件挺不错的,他还有memory访问控件,只是我没有试验过:http://www.zealsoftstudio.com/memaccess/download.html
      

  14.   

    我的第一种方法是进CPU RING0的方法,在CPU RING0级,应该可以直接对物理内存访问的
    ------
    这种方法我持保留态度, 即使是ring0也不可能通过汇编直接访问物理内存, 除非你暂时禁止分页. ring0是说你可以访问物理内存,不是说你能直接访问内存.
      

  15.   

    多谢捧场,请各位放松一下,观赏我的总结贴:http://community.csdn.net/Expert/topic/5242/5242379.xml?temp=.6433374
      

  16.   

    2.用ntdll的ZWopenS等,这是我用BCB写的一个类:
    -------
    这种方法我以前试过(试着dump smbios table), 但是不是每次都能成功(有些机器上能够成功,有些不能), 具体原因不详.