首页
社区
课程
招聘
[原创]替换SharedUserData
2007-1-18 14:01 19541

[原创]替换SharedUserData

2007-1-18 14:01
19541
版本:1.0

作者: xIkUg/RCT/CCG          xikug.xp [at] gmail [dot] com

我常去的网站:
http://debugman.wintoolspro.com
http://www.fcgchina.com
http://bbs.pediy.com
http://www.unpack.cn

说明:为了不至于混淆视线,本文以Win2000专业版,非3G,非PAE系统为蓝本进行讲述。其他系统稍有不同,但不在本文的讲述范围。

SharedUserData 是操作系统为每个进程提供的个共享数据结构,里面存放有很多重要的系统信息,如TickCount、系统时间、SystemRoot等。。。

其在DDK定义为:

#define KI_USER_SHARED_DATA         0xffdf0000
#define SharedUserData  ((KUSER_SHARED_DATA * const) KI_USER_SHARED_DATA)


他在内核中的地址是0xffdf0000,操作系统通过共享映射把这个结构以只读方式映射到每个进程的0x7ffe0000的地方。

操作系统为每个进程提供4G虚拟空间的访问能力(空间访问能力不代表空间容量),不过实际访问不了这么多,因为4G空间中低2G是用户空间,
高2G是系统空间(非3G模式的情况下),其中低2G空间中某些地址是受系统保护的,普通用户进程也不能访问,而高2G访问需要ring0权限。

操作系统采用分页机制来映射物理内存,并会为每个进程分配一个PT(Pages Table)和PD(Pages Directory),
每个进程的PTE(Page Table Entry)和PDE(Page Directory Entry)通常映射了不同的物理页,所以不同进程的同一虚拟地址的内容是不相干的。
上面说的“通常”也有例外,那就是不同进程的PTE或PDE也可能会映射相同的物理页,如果他们映射了相同的物理页,说明他们共享了一段内存,
SharedUserData就是这个例外,每个进程用户空间的0x7ffe0000都以只读方式映射到相同的物理页面上,而这个物理页面上就是KUSER_SHARED_DATA结构
的数据,因此操作系统上的每个进程都有一个这个结构,而操作系统只需要维护这一个结构就行了。

实际上在Windows NT操作系统的实现中大量的使用了这种内存共享机制,分页机制处理也远比这个我说的复杂,具体可参考《JIURL玩玩Win2k内存篇》。

我们回到我们的主题,我们要替换指定进程的SharedUserData。了解了上面我说的共享机制,可能你已经知道了,使进程的0x7ffe0000不要映射到0xffdf0000
指向的物理页,而是映射到我们自己的物理内存页就行了。没错。。。话是这么说,但实际上Windows的内存管理机制相当复杂,我在具体实现想法的时候碰
到了许多问题,真正彻底解决这些问题花了我足足两周的时间,其中最棘手的问题的是:
1. 死锁问题
2. 内存被置换到Standby链表上去的问题

事出必有因,我坚信操作系统能做到的事情我们也同样能做到。通过不断的调试操作系统代码终于解决了各种问题,现在我的代码能在2000-2003的系统下跑起来了。

我们需要定义一些结构,PTE和PDE的基地址如下:

#define PTE_BASE    0xC0000000
#define PDE_BASE    0xC0300000


根据虚拟地址得到PTE的地址和PDE的地址:

#define GetPteAddress(va) ((PMMPTE)(((((ULONG)(va)) >> 12) << 2) + PTE_BASE))
#define GetPdeAddress(va)  ((PMMPTE)(((((ULONG)(va)) >> 22) << 2) + PDE_BASE))


我们需要分配一段非分页内存,必须以小页边界(4K)对齐,0x7ffe0000也以页对齐的:

ProcessPTE->p = ExAllocatePool(NonPagedPool, PAGE_SIZE)


现在我们就替换SharedUserData,并把日期锁定:

RtlCopyMemory(ProcessPTE->p, SharedUserData, sizeof(KUSER_SHARED_DATA));
pMySharedData = ProcessPTE->p;
RtlTimeToTimeFields((PLARGE_INTEGER)&pMySharedData->SystemTime, &TimeFields);
TimeFields.Year = 2007;
TimeFields.Month = 1;
TimeFields.Day = 1;
RtlTimeFieldsToTime(&TimeFields, (PLARGE_INTEGER)&pMySharedData->SystemTime);
pMySharedData->SystemTime.High2Time = pMySharedData->SystemTime.High1Time;


这样日期就被我们锁定在2007-1-1了。。。,现在我们要让时间继续走,于是建一个系统线程去更新SystemTime:

pSharedData = ProcessPTE->p;
RtlTimeToTimeFields((PLARGE_INTEGER)&SharedUserData->SystemTime, &TimeFields);
TimeFields.Year = 2007;
TimeFields.Month = 1;
TimeFields.Day = 1;
RtlTimeFieldsToTime(&TimeFields, (PLARGE_INTEGER)&pSharedData->SystemTime);
pSharedData->SystemTime.High2Time = pSharedData->SystemTime.High1Time;


测试一下,我以现程序有些问题,在被替换进程的窗口最小化的时候会让系统死锁,是于我中断下来,查看调用堆栈:

80473ae0 8046950e 00000001 82060102 000000d1 nt!RtlpBreakWithStatusInstruction
80473ae0 80069b02 00000001 82060102 000000d1 nt!KeUpdateSystemTime+0x13e
80473b64 80464b59 0000000e 00000000 00000000 hal!HalProcessorIdle+0x2
80473b68 00000000 00000000 00000000 00000000 nt!KiIdleLoop+0x10


系统在这里空转, 查看我们目标进程的调用堆栈:

be482a50 8042d87a 00cc0020 00000000 0000032c nt!KiSwapThread+0xc5
be482a78 a007dbad 00000001 00000000 be482a8c nt!KeDelayExecutionThread+0x180
be482a94 a007e005 00000001 a03106e0 01010057 win32k!UserSleep+0x2b
be482af4 a007e11a 0000032c 01010057 000000c0 win32k!xxxAnimateCaption+0x229
be482b48 a004d809 a0332cf8 00000003 a0332cf8 win32k!xxxDrawAnimatedRects+0xc8
be482c20 a00151d3 e1f32468 00000006 00010000 win32k!xxxMinMaximize+0x1fc
be482c48 a003e872 00000010 00010006 00000112 win32k!xxxShowWindow+0x147
be482c7c a008977d a0332cf8 0000f020 00000000 win32k!xxxSysCommand+0x2a6
be482cdc a00045fa a0332cf8 00000112 0000f020 win32k!xxxDefWindowProc+0xc43
be482cf0 a00045e1 a0332cf8 00000112 0000f020 win32k!xxxWrapDefWindowProc+0x15
be482d0c a0004588 a0332cf8 00000112 0000f020 win32k!NtUserfnDWORD+0x25
be482d40 80465691 00050100 00000112 0000f020 win32k!NtUserMessageCall+0x89
be482d40 77df37e7 00050100 00000112 0000f020 nt!KiSystemService+0xc4
0012fe0c 00000000 00000000 00000000 00000000 +0x77df37e7


xxxAnimateCaption之上是UserSleep,这看起来是一个循环,于是我们转去对xxxAnimateCaption进行调度,发现之所以在这里卡住,是因为
我们没有更新SharedUserData的TickCountLow,而xxxAnimateCaption会从0x7ffe0000取这个值来画出动画效果,
具体可看xxxAnimateCaption的反汇编代码,比较多,我就不列出来了。。。
当然光是一个死循环还不至于让系统死锁,我们再向上看,xxxDrawAnimatedRects的时候调用GetDCEx导致锁定一个对象,具体见函数
GreLockDisplay的代码。而一个死循环导致了系统一直没有机会ReleaseDC。系统会不断的等待这个对象,所以导致了系统的死锁。

通过调试,我们发现GreLockDisplay锁定的对象是0x81e1bc08,

kd> dt 0x81e1bc08 _ERESOURCE -b
   +0x000 SystemResourcesList : _LIST_ENTRY [ 0x81e1bbc8 - 0x81e1bd08 ]
      +0x000 Flink            : 0x81e1bbc8 
      +0x004 Blink            : 0x81e1bd08 
   +0x008 OwnerTable       : (null) 
   +0x00c ActiveCount      : 1
   +0x00e Flag             : 0x80
   +0x010 SharedWaiters    : 0x81cf4168 
   +0x014 ExclusiveWaiters : 0x81e0c068 
   +0x018 OwnerThreads     : 
    [00] _OWNER_ENTRY
      +0x000 OwnerThread      : 0x81d0d020
      +0x004 OwnerCount       : 1
      +0x004 TableSize        : 1
    [01] 
      +0x000 OwnerThread      : 0
      +0x004 OwnerCount       : 0
      +0x004 TableSize        : 0
   +0x028 ContentionCount  : 0x5e9
   +0x02c NumberOfSharedWaiters : 0
   +0x02e NumberOfExclusiveWaiters : 3
   +0x030 Address          : (null) 
   +0x030 CreatorBackTraceIndex : 0
   +0x034 SpinLock         : 0


0x81d0d020就是我们的线程,从这个结构来看,我们的线程占有了一个对象,而还有3个等待者(NumberOfExclusiveWaiters : 3),
这3个等待者在ExclusiveWaiters : 0x81e0c068指向的地方,

kd> dt 0x81e0c068 _KEVENT -b
   +0x000 Header           : _DISPATCHER_HEADER
      +0x000 Type             : 0x1 ''
      +0x001 Absolute         : 0 ''
      +0x002 Size             : 0x4 ''
      +0x003 Inserted         : 0 ''
      +0x004 SignalState      : 0
      +0x008 WaitListHead     : _LIST_ENTRY [ 0x81cf962c - 0x81da15cc ]
         +0x000 Flink            : 0x81cf962c 
         +0x004 Blink            : 0x81da15cc 

kd> dt 0x81cf962c _KWAIT_BLOCK -b
   +0x000 WaitListEntry    : _LIST_ENTRY [ 0x81e0ca6c - 0x81e0c070 ]
      +0x000 Flink            : 0x81e0ca6c 
      +0x004 Blink            : 0x81e0c070 
   +0x008 Thread           : 0x81cf95c0 
   +0x00c Object           : 0x81e0c068 
   +0x010 NextWaitBlock    : 0x81cf9674 
   +0x014 WaitKey          : 0
   +0x016 WaitType         : 1

kd> dt 0x81e0ca6c _KWAIT_BLOCK -b
   +0x000 WaitListEntry    : _LIST_ENTRY [ 0x81da15cc - 0x81cf962c ]
      +0x000 Flink            : 0x81da15cc 
      +0x004 Blink            : 0x81cf962c 
   +0x008 Thread           : 0x81e0ca00 
   +0x00c Object           : 0x81e0c068 
   +0x010 NextWaitBlock    : 0x81e0cab4 
   +0x014 WaitKey          : 0
   +0x016 WaitType         : 1

kd> dt 0x81da15cc _KWAIT_BLOCK -b
   +0x000 WaitListEntry    : _LIST_ENTRY [ 0x81e0c070 - 0x81e0ca6c ]
      +0x000 Flink            : 0x81e0c070 
      +0x004 Blink            : 0x81e0ca6c 
   +0x008 Thread           : 0x81da1560 
   +0x00c Object           : 0x81e0c068 
   +0x010 NextWaitBlock    : 0x81da1614 
   +0x014 WaitKey          : 0
   +0x016 WaitType         : 1


其中两个线程0x81da1560和0x81cf95c0是VMWare的关键线程(看来VMWare需要跟系统配合运行的,并不是完全模拟硬件),
另一个线程0x81e0ca00是csrss.exe(csrss负责win32子系统)的关键线程。以上就是导致系统死锁的根本原因。
(windbg中可以用!locks来搜索系统中所有的锁,当然有时候搜索出来的结果比较多,还没有手工来得直接)

要解决这个问题,我们可采用以下方法:
1. 关闭系统的动画效果
2. 我们来更新这个值

如果采用1的方法,我难保系统的其他地方没有用SharedUserData的TickCountLow这个值来做循环因子,因此最彻底的方法是我们自己更新这个值,于是
我们在我们的系统线程中加入:

pSharedData->TickCountLow = SharedUserData->TickCountLow;


现在死锁问题解决了,我们又迎来了一个新的问题:我们申请的用于替换的内存会被置换到Standby链表上去。当有程序要访问0x7ffe0000的时候就会非法访问。

用Arm加壳的程序在调用SetProcessWorkingSetSize或程序最小化时会出现这个问题。这是因为Windows分页机制造成的,Windows会根据需要调整进程的工作集空间。
什么情况下内存会置换出去呢?我们申请的明明是非分页内存是不允许被置换出去的,为什么会出现这种情况呢?事出必有因,什么时候Windows会调整进程工作集,
调整工作集的条件是什么?为什么有些页不会被置换出去?要解答这些问题,我们需要分析Windows的相关代码。

通过不断的调试分析,最终我把相当代码锁定到了MiFreeWsle函数,拿出我们手里都有的那份代码,经过简化后,关键的地方就是如下:

if ((Pfn1->u2.ShareCount > 1) &&
    (Pfn1->u3.e1.PrototypePte == 0)) {

    UNLOCK_PFN (OldIrql);
    return FALSE;
}

//
// Found a candidate, remove the page from the working set.
//
MiEliminateWorkingSetEntry (WorkingSetIndex,
			PointerPte,
			Pfn1,
			Wsle);


如果一个页的ShareCount(共享数)为1,就会调用MiEliminateWorkingSetEntry把页从工作集中移走。

了解了吧?跃然我们在系统空间中申请了一页非分页内存,但映射到0x7ffe000后相关PfnDatabase并不知道,而我们这页的共享数为1,所以0x7ffe000会被置换走.
当有程序试图访问0x7ffe0000的时候会产生页访问异常。此时系统不知道如何处理这个突如其来的异常而导致系统崩溃。

所以要解决这个问题,我们需要手工改PfnDatabase的SharedCount为大于1的数,这里蛮干,我机器上PfnDatabase的地址是0x820bf000。

PMMPFN      MmPfnDatabase = (PMMPFN)0x820bf000;

PointerPte = GetPteAddress (0x7ffe0000);
PageFrameNumber = PointerPte->u.Hard.PageFrameNumber;
Pfn1 = &MmPfnDatabase[PageFrameIndex];
Pfn1->u3.e2.ReferenceCount = 2;
Pfn1->u2.ShareCount = 2;
Pfn1->u3.e1.PrototypePte = 0;


到这里我们已经把重要的问题都解决了,但还有一个比较重要的问题。。。有借有还,在进程退出的时候我们需要还原原始的PTE,这样就不至于让系统产生PFN_LIST_CORRUPT异常了。

结束语:我们在进程创建的时候就替换SharedUserData,可根据普通应用程序的特性做出一些与时间有关的应用,如加速,减速,Anti GetTickCount等。。。

感谢:Forever, fly, heXer, jwh51, +dEMON, kanxue, shoooo, 南蛮妈妈, sucsor, cnbragon, 堀北真希, winroot,
        DarkNess0ut and RCT & FCG & CCG all Members and you.

[培训]《安卓高级研修班(网课)》月薪三万计划,掌 握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞7
打赏
分享
最新回复 (21)
雪    币: 154
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jazhi 2007-1-18 14:04
2
0
虽然8懂但也得顶
雪    币: 896
活跃值: (4039)
能力值: ( LV9,RANK:3410 )
在线值:
发帖
回帖
粉丝
fly 85 2007-1-18 14:15
3
0
等你的“月光宝盒”工具
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sucsor 2007-1-18 14:16
4
0
顶.xikug强.
雪    币: 257
活跃值: (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
ViperDodge 1 2007-1-18 14:17
5
0
  传说中的停止时间,顶
雪    币: 254
活跃值: (126)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
heXer 3 2007-1-18 14:25
6
0
有困难,找西裤
雪    币: 214
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
绫濑遥 1 2007-1-18 14:28
7
0
ya me de
雪    币: 32403
活跃值: (18860)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 8 2007-1-18 14:46
8
0
问:xIkUg怎么这以强?
答:因为xIkUg是传说中的第八个man
雪    币: 255
活跃值: (266)
能力值: ( LV12,RANK:220 )
在线值:
发帖
回帖
粉丝
WiNrOOt 5 2007-1-18 15:18
9
0
深奥的看不懂
雪    币: 625
活跃值: (1057)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
xzchina 1 2007-1-18 15:31
10
0
完全不懂.
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
softworm 30 2007-1-18 15:46
11
0
感谢牛人解惑,西裤哥能否给份源码?

先收藏,等眼睛好了再学习,我竟然又不幸惹到了红眼病
雪    币: 116
活跃值: (220)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
xIkUg 9 2007-1-18 16:16
12
0
现在是实验代码,写得很丑,不好意思拿出来。。。很多地方都是硬编码。。。
如果你要的话请联系我
雪    币: 50
活跃值: (145)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
zhaoocn 7 2007-1-18 18:32
13
0
看了两眼就有点晕
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
softworm 30 2007-1-18 19:25
14
0
请发到softworm2003@hotmail.com
谢谢!
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
metalqiang 2007-1-19 00:35
15
0
好深奥
很难理解哦
雪    币: 201
活跃值: (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
fexsilence 2007-1-19 02:07
16
0
感谢楼主,学习了。
雪    币: 214
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
colboy 2007-1-19 12:43
17
0
学习!!!!!!!
我也要一份
coolcute@gmail.com
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
softworm 30 2007-1-19 15:11
18
0
感谢xIkUg,我收到了
雪    币: 603
活跃值: (617)
能力值: ( LV12,RANK:660 )
在线值:
发帖
回帖
粉丝
prince 16 2007-1-19 16:07
19
0
顶...
雪    币: 196
活跃值: (135)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
thinkSJ 4 2007-1-19 21:28
20
0
再顶....
雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
qiweixue 19 2007-1-20 13:05
21
0
最初由 kanxue 发布
问:xIkUg怎么这以强?
答:因为xIkUg是传说中的第八个man





man

雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
qiweixue 19 2007-1-20 13:14
22
0
最初由 kanxue 发布
问:xIkUg怎么这以强?
答:因为xIkUg是传说中的第八个man





man

游客
登录 | 注册 方可回帖
返回