对于Windows的分页机制, 我一直还是比较自信的, 但是今天我发现在重写NewBluePill的X64分页机制到Win32上面的时候, 居然感觉有些棘手, 看来啊, 很多东西还是不是看上去那样简单, 难点主要集中在原来倒腾没有分页的时候, 没有很好的和Windows结合起来, 现在面对着Windows, 感觉有些手生. 而且现在分页机制比原来多了. 原来只倒腾过一种, 最简单的, 现在有PAE, 还有X64的各种.. 搅在一起. 有些恶心.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//=========================================================================== 
内核调试的时候, 切换到某个用户态进程空间,

1:使用!process 0 0 获取用户空间的所有的进程的信息

!process 0 0

2: 使用.process /p + 你需要断的应用程序的EProcess地址,切换到应用程序的地址空间

.process /p 0x80a02a60

3: 重新加载user PDB文件

.reload /f /user

4: 使用非侵入式的切换进程空间

.process /i /p 0x80a02a60

//=========================================================================== 
线性地址到物理地址的转换没有开启PAE..CR3低12位无效

kd> r cr4
cr4=000006d9
kd> .formats 06d9
Evaluate expression:
Hex: 000006d9
Decimal: 1753
Octal: 00000003331
Binary: 00000000 00000000 00000110 11011001

可以看到CR4的第5位为0, 所以是没有开启PAE物理地址扩展的..

kd> du 000acfe0 000acfe0 "123654789."

000acfe0里面存放的就是我们要转换的数据, 那么 000acfe0 = 00000000 00001010 11001111 11100000

页目录索引0 页表索引AC 偏移FE0 0000000000 0010101100 111111100000

CR3 = 193bd000 + 页目录索引0

kd> !dd 193bd000 == PDE C0300000
#193bd000 19500067 19679067 194d3067 00000000
#193bd010 192bb067 00000000 00000000 00000000
#193bd020 00000000 00000000 00000000 00000000
#193bd030 00000000 00000000 00000000 00000000

19500067 = 00011001 01010000 00000000 01100111

页表基址 G(全局页) 页大小(0=4K) D(页被写过) A(被访问过) PageCache WriteBack U/S(1表示是用户页) R/W(可读可写 ) 是否在物理内存(P) 00011001010100000000 000 0 0 1 1 0 0 1 1 1

页表基址是19500000 加上页表索引 * 4

kd> !dd 19500000+AC*4 PTE C0000000 + AC*4

#195002b0 19382067 194d5067 193d6067 195d8067
#195002c0 19599067 1975a067 1975b067 1961c067
#195002d0 1989d067 00000080 00000000 00000000
#195002e0 00000000 00000000 00000000 00000000

页目录内存为19382067, 高20位也就是19382000, 属性和上面一样.

kd> !dd 19382000+FE0
#19382fe0 00320031 00360033 00340035 00380037
#19382ff0 002e0039 00000000 00000000 00000000
#19383000 00000000 00000000 00000000 00000000
#19383010 00000000 00000000 00000000 00000000
#19383020 016a0169 016c016b 016e016d 0170016f
#19383030 01720171 01740173 01760175 01780177
#19383040 017a0179 017c017b 017e017d 0180017f
#19383050 01820181 01840183 01860185 01880187

kd> !du 19382000+FE0
#19382fe0 "123654789."

OK这里就得到物理地址了.. 也就是说000acfe0虚拟地址对应的物理地址是19382fe0, 也可以使用!vtop来转换

kd> !vtop 193bd 000acfe0

Pdi 0 Pti ac
000acfe0 19382000 pfn(19382)

//=========================================================================== 
线性地址到物理地址的转换开启PAE..在启动参数加/PAE 在开启了PAE的时候, Windows规定了虚拟地址的c0600000存放PDE. c0000000存放PTE. 注意这里的CR3只有低5位无效

r cr4 = 6f9 00000000 00000000 00000110 11111001开启了PAE物理地址扩展

kd> du 000ace70 000ace70 "65884265896."

000ace70 里面存放的就是我们要转换的数据, 那么 000ace70 = 00000000 00001010 11001110 01110000

页目录指针表 页目录表索引0 页表索引AC 偏移E70 00 000000000 010101100 111001110000

CR3 = 087002e0 + 页目录索引0

kd> !dq 087002e0
# 87002e0 00000000`12a59001 00000000`12a1a001
# 87002f0 00000000`12b9b001 00000000`12ad8001
# 8700300 00000000`f8dcc320 00000000`00000000
# 8700310 00000000`00000000 00000000`00000000

12a59001 = 00010010 10100101 10010000 00000001

页目录表基址是12a59000 加上页表索引 * 8 PDE

kd> !dq 12a59000+0*8 = C0600000 + 0 * 8

#12a59000 00000000`129df067 00000000`129b5067
#12a59010 00000000`12c1b067 00000000`00000000
#12a59020 00000000`12979067 00000000`12a7a067
#12a59030 00000000`00000000 00000000`00000000

页表索引内存为00000000`129df067, 高20位也就是129df000, 属性和上面一样. PTE

kd> !dq 129df000+AC*8 = C0000000+AC*8

#129df560 00000000`12929067 00000000`1297d067
#129df570 00000000`1297e067 00000000`12e80067
#129df580 00000000`12b41067 00000000`12a82067
#129df590 00000000`12b03067 00000000`12ac4067

kd> !du 12929000+E70
#12929e70 "65884265896."

OK这里就得到物理地址了.. 也就是说000ace70虚拟地址对应的物理地址是12929e70, 也可以使用!vtop来转换, 不过这个有PAE的时候 要注意了 kd> !vtop 12a59000 000ace70

这样才能够转换成功. 得CR3取一次.

//===========================================================================

差不多就这些了. 写代码的时候, 我们就要将整个Windows的页面映射情况, Copy一份出来, 完了之后再将Windows对自己的映射这份删除掉, 这样就可以实现, 我可以打你的脸, 你不能打我的脸. 暴力. 太暴力了.

当然说到CopyWindows的页表的话, 我还想多说两句, 首先, 要根据PAE开启和位开启的情况做不同处理, 还有页面是大页面, 小页面也要考虑到. 其实只需要映射操作系统这边的内存就可以了. 用户态的基本没有什么用吧. 我们只需要能够实现调用到系统的一些函数就可以.. 恩, 思路就是这样!!!