本论坛为只读模式,仅供查阅,不能注册新用户,不能发帖/回帖,有问题可发邮件 xikug.xp (^) gmail.com
查看: 5673|回复: 6

[原创/讨论] 读写物理地址内容的几种方法(修改版) [复制链接]

Rank: 1

发表于 2008-7-9 15:43:35 |显示全部楼层

[原创/讨论] 读写物理地址内容的几种方法(修改版)
关键字:windows内核,物理地址



(修改偶很久很久以前写的一些小总结,高手飘过)

各位知道在windows NT中,如果已知虚拟地址可以通过进程页表或者内核
提供
的MmGetPhysicalAddress来取得对应的物理地址。

现在我们反过来思考一下,如果已知一个物理内存地址 (假设地址有效),如何取得物理地址中的内容呢?经过一番思考和尝试后,偶发现一个方法,但我这里先卖个关子,为什么呢?因为我觉得这个方法貌似有点“不务正业”。经过和csdn的各位朋友讨论之后,加上本人的一共总结出4种方法,现在想和大家分享一下。:)

下面不再废话,立即进入正题。



[方法1]:使用内核提供的MmMapIoSpace函数

原来内核早就提供了很简单的接口,就是MmMapIoSpace函数,不过在DDK
文档中
看到该函数的说明如下:



PVOID MmMapIoSpace(

IN PHYSICAL_ADDRESS PhysicalAddress,

IN ULONG NumberOfBytes,

IN MEMORY_CACHING_TYPE CacheType );



它只有3个形参,这和实际Masm32v9.0声明的4个形参有所不同:

MmMapIoSpace    proto stdcall :DWORD, :DWORD, :DWORD,\ :DWORD


为什么参数个数会有不同呢?
原因是MmMapIoSpace第一个参数传递的是一个结构而非结构的指针,而
该结构实
际的大小是 2 个双字,结果在masm32中表现为总共4个 dword
参数。


这样的话调用就很简单了:


invoke MmMapIoSpace,_phyaddr_low,0,4,MmNonCached


若成功该函数返回影射后的虚拟地址,否则返回NULL。这样就可以间接
达到读取
物理地址中内容的目的。



[方法2]:通过操作物理内存对象来完成到虚拟地址的影射

简单的说这个方法的基本思想如下:

a : 取得本进程句柄;
b : 取得物理内存对象的句柄;
c : 将物理内存地址影射到进程的虚拟地址空间中。

下面是偶从C改写后的汇编关键代码:

GetValByPhyAddr proc uses esi edi ebx _phyaddr

local hpmem:dword
local hp:dword
local dwsize:dword
local dwbase:dword
local dwret:dword
local cid:CLIENT_ID
local stLR:LARGE_INTEGER
local ObjAttrsMem:OBJECT_ATTRIBUTES
local ObjAttrsP:OBJECT_ATTRIBUTES


mov dwbase,0

mov dwsize,4

push _phyaddr

pop stLR.LowPart

mov stLR.HighPart,0

invoke PsGetCurrentProcessId

mov cid.UniqueProcess,eax

mov cid.UniqueThread,0

invoke InitObjAttrs,addr ObjAttrsP,0

Invoke ZwOpenProcess,addr hp,PROCESS_DUP_HANDLE,\

addr ObjAttrsP,addr cid

invoke InitObjAttrs,addr ObjAttrsMem,addr devphymem

Invoke ZwOpenSection,addr hpmem,\

SECTION_MAP_READ or SECTION_MAP_WRITE,\

addr ObjAttrsMem

invoke ZwMapViewOfSection,hpmem,hp,addr dwbase,\

0,4,addr stLR,addr dwsize,1,\

MEM_TOP_DOWN,PAGE_READWRITE

mov edx,dwbase

mov eax,[edx] ;取得影射后对应虚拟地址中的内容

mov dwret,eax

invoke ZwUnmapViewOfSection,hp,addr dwbase

invoke ZwClose,hpmem

invoke ZwClose,hp

mov eax,dwret

ret

GetValByPhyAddr endp




[方法3]:在ring3下打开物理内存对象读写之

偶在ring3下无驱进入ring0的文章中,间接使用了该方法,原理很简单:

默认只有 System 用户有写物理内存的权限,administrators 组的用户
只有读的权
限,但是通过修改用户安全对象中的DACL 可以增加写的权限。



_SetPhyMemDACLs      proc       uses ebx edi esi \
                                       _hPhymem:HANDLE,\
                                       _ptusrname:dword

    local  @dwret:dword
    local  @htoken:HANDLE
    local  @hprocess:HANDLE
    local  @个
    local  @OldDACLs:PACL
    local  @SecurityDescriptor:PSECURITY_DESCRIPTOR
    local  @Access:EXPLICIT_ACCESS

    mov     @dwret,FALSE
    invoke RtlZeroMemory,addr @NewDACLs,sizeof @NewDACLs
           invoke RtlZeroMemory,addr @SecurityDescriptor,\
           sizeof @SecurityDescriptor

    invoke GetSecurityInfo,_hPhymem,SE_KERNEL_OBJECT,\
           DACL_SECURITY_INFORMATION,NULL,NULL,\
           addr @OldDACLs,NULL,\
           addr @SecurityDescriptor

    .if eax != ERROR_SUCCESS
           jmp SAFE_RET
    .endif

    invoke RtlZeroMemory,addr @Access,sizeof @Access

    mov     @Access.grfAccessPermissions,SECTION_ALL_ACCESS
    mov     @Access.grfAccessMode,GRANT_ACCESS
    mov     @Access.grfInheritance,NO_INHERITANCE
    mov     @Access.stTRUSTEE.MultipleTrusteeOperation,\
           NO_MULTIPLE_TRUSTEE

    mov     @Access.stTRUSTEE.TrusteeForm,TRUSTEE_IS_NAME
    mov     @Access.stTRUSTEE.TrusteeType,TRUSTEE_IS_USER
    push   _ptusrname
    pop     @Access.stTRUSTEE.ptstrName

    invoke GetCurrentProcess
    mov     @hprocess,eax
    invoke OpenProcessToken,@hprocess,TOKEN_ALL_ACCESS,\
           addr @htoken

    invoke SetEntriesInAcl,1,addr @Access,\
           @OldDACLs,addr @NewDACLs

    .if eax != ERROR_SUCCESS
           jmp SAFE_RET
    .endif

    invoke SetSecurityInfo,_hPhymem,SE_KERNEL_OBJECT,\
           DACL_SECURITY_INFORMATION,NULL,NULL,\
           @NewDACLs,NULL

    .if eax != ERROR_SUCCESS
           jmp SAFE_RET
    .endif

    mov     @dwret,TRUE

SAFE_RET:

    .if @NewDACLs != NULL
           invoke LocalFree,@NewDACLs
           mov @NewDACLs,NULL
    .endif

    .if @SecurityDescriptor != NULL
           invoke LocalFree,@SecurityDescriptor
           mov @SecurityDescriptor,NULL
    .endif

    mov     eax,@dwret

    ret

_SetPhyMemDACLs      endp


然后通过一个"轻量级"的GetPhysicalAddress来取得虚地址对应的物理
地址.

[backcolor=rgb(204,]@GetPhymemLite    proc   uses esi edi ebx         _vaddr

    local  @dwret:dword

    mov     @dwret,FALSE

    .if _vaddr < 80000000h
           jmp quit
    .endif

    .if _vaddr >= 0a0000000h
           jmp quit
    .endif

    mov     eax,_vaddr
    and     eax,01ffff000h       ;or sub eax,80000000h
    mov     @dwret,eax
quit:

    mov     eax,@dwret
    ret

@GetPhymemLite    endp
[/table]


[方法4]:关闭CPU分页机制达到直接读取物理地址的目的


最后这种方法就是偶前面卖关子的方法,这种方法不依赖于OS,了解
保护模
式的朋友都知道:进入保护模式后如果关闭分页机制则CPU就会将
线形地址直
接解释成物理地址。

关闭分页很简单,只需3行汇编代码:


mov eax,cr0
and eax,7fffffffh
mov cr0,eax


打开分页同样很简单:

mov eax,cr0
or eax,80000000h
mov cr0,eax


既然这么简单,为什么我在前面说这个方法有些“绕”呢?
原因之一是如果仅仅关闭分页就会使原来依赖于分页的windows变得一
团糟,马上
会发生著名的蓝屏现象,不爽哦!

那要怎么做呢?经过偶的尝试,发现有3点非常重要:


1 : 要保证windows处在较高的IRQL级别,防止关闭分页后被打断;

2 : 要将处理分页、读取物理地址的指令块放到线形地址等于物理地址
    的页中;


3 : 要做好数据的恢复工作。


第1点和第3点都容易理解,可能有人会疑问第2点,为什么要放到线形
地址等于物理
地址的空间中呢?原因其实也很简单,就是一旦关闭分页,
所有地址都将以物理地
址来解释,如果先前的虚拟地址和物理地址不相
同那么很可能就会发生执行错误指
令流的问题。(当然也可以不相同,
只要你能保证关分页后能执行正确指令即可。)


那么如何将物理地址和虚拟地址设置为相同呢?偶的方法是将虚拟地址
对应地页表
PTE中的物理页基址改为包容即可。

偶随机选择了一个虚拟地址 0x1c00000 ,为了简单它正好是页面的一个基址。下面偶构造了它的PTE使得其中的物理也帧指向它:


mov edx,0ffdf0000h
mov eax,[edx]
mov tmp0,eax ; 保存原始值
mov dword ptr [edx],1c00007h

;初始化PTE

mov edx,0c03ff7c0h
mov eax,[edx]
and eax,0fffff000h
or eax,7h
mov edx,0c030001ch
mov ebx,[edx]
mov tmp1,ebx ; 保存原始值
mov dword ptr [edx],eax

;刷新TLB

mov eax,cr3
mov cr3,eax


最后两句指令在偶前一篇: <<Windows内核编程研究一:改变进程PTE>>
中有过说明,
这里就不解释了。结束了PTE的设置后,下面就是将指令块
拷贝到指定位置,代码如下:


mov ecx,slen
mov esi,subasm
mov edi,1c00000h
cld
rep movsb

而subasm指令块完成的就是实际设置分页和读取物理地址内容的工作,
如下:


subasm:
mov eax,cr0
and eax,7fffffffh ;关闭分页机制
mov cr0,eax
jmp SHORT PageC

PageC:

mov edx,[ecx]
mov eax,cr0
or eax,80000000h ;打开分页机制
mov cr0,eax
jmp SHORT PageO

PageO:

jmp edi

slen = $ - subasm


借用一句话作为结尾“…一切都那么清楚明白,然而却无法透彻理解。
理解就是改
变,就是超越自己已有的认识。”

感谢CSDN热心朋友提供的方法和意见,给偶很好
的启发。

末尾,感谢各位观赏,如有误谬之处请不吝指出,不胜感谢。希望有同
好的朋友能
够一起讨论,一起交流。

Rank: 1

发表于 2008-7-9 15:47:23 |显示全部楼层
修改不了后面代码的颜色?

Rank: 2

发表于 2008-7-9 22:15:35 |显示全部楼层
是侯佩本人~~顶牛人

Rank: 1

发表于 2008-7-10 13:52:57 |显示全部楼层
只用标准接口MmMapIoSpace,稳定,兼容,其他的换个系统改来改去的,做ms做过的事,还没有他做的稳定和兼容.以后倾向越来越标准的接口了

Rank: 1

发表于 2008-7-10 19:30:37 |显示全部楼层
我要VISTA能用的

Rank: 1

发表于 2008-7-10 19:54:36 |显示全部楼层
引用第4楼xiaohuangran于2008-07-10 16:30发表的  :
我要VISTA能用的
第一种和第四种方法都可以在 Vista 下使用呀。

Rank: 1

发表于 2013-5-7 09:18:19 |显示全部楼层
很不错,好好学学
您需要登录后才可以回帖 登录 | 立即加入

Archiver|手机版|第8个男人 - 论坛为只读模式,仅供查阅

GMT+8, 2019-4-19 03:33 , Processed in 0.028847 second(s), 8 queries .

Design by pvo.cn

© 2011 Pvo Inc.

回顶部