在高端调试论坛上看到有人调试calc.exe的,我也依样画葫芦调试notepad.exe:定位notepad.exe缓存所在的物理页面进而修改文本内容。
文本原有的内容如下:
目前windbg正在调试内核,调试器不在notepad.exe进程上下文中,为了达到目的,需要枚举系统中的进程并切换到notepad.exe
!process 0 0 ;枚举系统所有进程
Failed to get VAD root ;notepad.exe的KPROCESS结构
PROCESS 82417620 SessionId: 0 Cid: 03e8 Peb: 7ffd7000 ParentCid: 05a8
DirBase: 09800340 <----notepad.exe的页目录表 ObjectTable: e19962f8 HandleCount: 50.
Image: notepad.exe
向notepad.exe切换
kd> .process 82455ce8
Implicit process is now 82455ce8
WARNING: .cache forcedecodeuser is not enabled
kd> r cr3 ;cr3保存当前进程的页目录表
cr3=00b1f000
cr3和前面!process输出的页目录表不同,为什么呢?.process 82455ce8 仅切换了KPROCESS结构,并没有切换进程上下文,因此页目录表与预期不同。这时需要用入侵式切换:
kd> .process /i /p 82417620 ;/i参数实现入侵式切换
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g ;上面windbg提示说要按F5,其实就是让系统进行线程切换,当windbg再次回到交互界面时才正式切换到notepad.exe进程空间中
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
80528bdc cc int 3
kd> r cr3 ;notepad.exe的页目录表 和前面!process的输出相同
cr3=09800340
kernel调试时windbg并不会加载用户态dll,但是切换到指定进程后可以加载用户态调试符号,如下:
kd> .reload /f /user ntdll
kd> lm m ntdll
start end module name
7c920000 7c9b3000 ntdll M (pdb symbols) e:\symbol\xp_fre\offline\dll
\ntdll.pdb
回到正文,文件目前显示的内容一定在内存中,但我并不知道保存这些文本的内存地址,搜索整个用户空间固然是个不错的拌饭,但这个效率太低了,应该缩小范围再查找特定内容。怎么缩小范围呢?可以猜测应该保存在进程堆上,毕竟栈空间这么有限。顺着这个思路,我们查看一下notepad.exe已分配的堆 !heap -a用于列出堆:
kd> !heap -a
HEAPEXT: Unable to get address of *ntdll!RtlpGlobalTagHeap.
Index Address Name Debugging options enabled
1: 000a0000
Segment at 000a0000 to 001a0000 (00018000 bytes committed)
2: 001a0000
Segment at 001a0000 to 001b0000 (00006000 bytes committed)
3: 001b0000
Segment at 001b0000 to 001c0000 (00003000 bytes committed)
4: 00410000
Segment at 00410000 to 00420000 (00008000 bytes committed)
5: 00420000
Segment at 00420000 to 00430000 (00004000 bytes committed)
6: 00440000
Segment at 00440000 to 00480000 (00003000 bytes committed)
7: 00030000
Segment at 00030000 to 00040000 (00006000 bytes committed)
8: 003b0000
Segment at 003b0000 to 003c0000 (00008000 bytes committed)
Segment at 009c0000 to 00ac0000 (0007b000 bytes committed)
9: 00400000
Segment at 00400000 to 00410000 (00003000 bytes committed)
进程目前用了9个堆,我们先选第一个试试,s命令用于搜索内存地址
kd> s -u 000a0000 L00018000 "AABBCCDDEEFF"
000b0fb8 0041 0041 0042 0042 0043 0043 0044 0044 A.A.B.B.C.C.D.D.
000b13b8 0041 0041 0042 0042 0043 0043 0044 0044 A.A.B.B.C.C.D.D.
好像找到了相像的字符串,再用dd确认一下:
kd> du 000b0fb8
000b0fb8 "AABBCCDDEEFF"
好吧,地址000b0fb8 就是字符串"AABBCCDDEEFF"的首地址。到这其实已经能通过ed命令修改文本内容了。但是,我就不,我要查看和修改相应物理页面的内容。通过一个虚拟地址查找对应的物理页面至少有2种方式:1)手动在页目录表中查找,这个比较复杂;2)用!pte命令找到对应页表中记录的物理页面值,然后再在物理页面中找偏移。这里采用方式2)。
kd> !pte 000b0fb8
VA 000b0fb8
PDE at C0600000 PTE at C0000580 <---虚拟地址在页表中的地址是C000580
contains 0000000013072067 contains 8000000012F34067 <---C000580处记录的物理地址是<span style="font-family: Arial, Helvetica, sans-serif;">12F34067 ,其中最后3位067是物理页面的权限位</span>
pfn 13072 ---DA--UWEV pfn 12f34 ---DA--UW-V
最后准备获得物理地址上的内容,步骤是屏蔽物理地址的12F34067低12bit,然后与虚拟地址0xb0fb8的低12bit相加:
kd> !dd 0x12F34fb8
#12f34fb8 00410041 00420042 00430043 00440044
#12f34fc8 00450045 00460046 00000000 00000015
这与虚拟地址给出的结果一样,毕竟物理地址才是虚拟地址的载体:
kd> dd 000b0fb8
000b0fb8 00410041 00420042 00430043 00440044
000b0fc8 00450045 00460046 00000000 00000015
到了最后一步了,修改物理地址的内容:
kd> !ed 0x12F34fb8 00490049
刷新notepad后就能看到文本的变化。
===================================================================
后记,这个用来学习windbg命令,看来没什么用,但我一个同事通过这种方式实现了简单的文件内容加密。。。