首页
论坛
课程
招聘
[原创]SSDT Hook的妙用-对抗ring0 inline hook
2007-3-10 15:18 223804

[原创]SSDT Hook的妙用-对抗ring0 inline hook

2007-3-10 15:18
223804
*******************************************************
*标题:【原创】SSDT Hook的妙用-对抗ring0 inline hook  *
*作者:堕落天才                                        *
*日期:2007年3月10号                                   *
*声明:本文章的目的仅为技术交流讨论                    *
*******************************************************

1,SSDT
     SSDT即系统服务描述符表,它的结构如下(参考《Undocument Windows 2000 Secretes》第二章):
     typedef struct _SYSTEM_SERVICE_TABLE
     {
             PVOID   ServiceTableBase;        //这个指向系统服务函数地址表
             PULONG  ServiceCounterTableBase;
             ULONG   NumberOfService;         //服务函数的个数,NumberOfService*4 就是整个地址表的大小
             ULONG   ParamTableBase;
     }SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;   
     
     typedef struct _SERVICE_DESCRIPTOR_TABLE
     {
       SYSTEM_SERVICE_TABLE   ntoskrnel;  //ntoskrnl.exe的服务函数
       SYSTEM_SERVICE_TABLE   win32k;     //win32k.sys的服务函数,(gdi.dll/user.dll的内核支持)
       SYSTEM_SERVICE_TABLE   NotUsed1;
       SYSTEM_SERVICE_TABLE   NotUsed2;
     }SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
     
     内核中有两个系统服务描述符表,一个是KeServiceDescriptorTable(由ntoskrnl.exe导出),一个是KeServieDescriptorTableShadow(没有导出)。两者的区别是,KeServiceDescriptorTable仅有ntoskrnel一项,KeServieDescriptorTableShadow包含了ntoskrnel以及win32k。一般的Native API的服务地址由KeServiceDescriptorTable分派,gdi.dll/user.dll的内核API调用服务地址由KeServieDescriptorTableShadow分派。还有要清楚一点的是win32k.sys只有在GUI线程中才加载,一般情况下是不加载的,所以要Hook KeServieDescriptorTableShadow的话,一般是用一个GUI程序通过IoControlCode来触发(想当初不明白这点,蓝屏死机了N次都想不明白是怎么回事)。

2,SSDT HOOK
    SSDT HOOK 的原理其实非常简单,我们先实际看看KeServiceDescriptorTable是什么样的。   
    lkd> dd KeServiceDescriptorTable
    8055ab80  804e3d20 00000000 0000011c 804d9f48
    8055ab90  00000000 00000000 00000000 00000000
    8055aba0  00000000 00000000 00000000 00000000
    8055abb0  00000000 00000000 00000000 00000000   
    在windbg.exe中我们就看得比较清楚,KeServiceDescriptorTable中就只有第一项有数据,其他都是0。其中804e3d20就是
KeServiceDescriptorTable.ntoskrnel.ServiceTableBase,服务函数个数为0x11c个。我们再看看804e3d20地址里是什么东西:
    lkd> dd 804e3d20
    804e3d20  80587691 805716ef 8057ab71 80581b5c
    804e3d30  80599ff7 80637b80 80639d05 80639d4e
    804e3d40  8057741c 8064855b 80637347 80599539
    804e3d50  8062f4ec 8057a98c 8059155e 8062661f
    如上,80587691 805716ef 8057ab71 80581b5c 这些就是系统服务函数的地址了。比如当我们在ring3调用OpenProcess时,进入sysenter的ID是0x7A(XP SP2),然后系统查KeServiceDescriptorTable,大概是这样KeServiceDescriptorTable.ntoskrnel.ServiceTableBase(804e3d20) + 0x7A * 4 = 804E3F08,然后804E3F08 ->8057559e 这个就是OpenProcess系统服务函数所在,我们再跟踪看看:
    lkd> u 8057559e
    nt!NtOpenProcess:
    8057559e 68c4000000      push    0C4h
    805755a3 6860b54e80      push    offset nt!ObReferenceObjectByPointer+0x127 (804eb560)
    805755a8 e8e5e4f6ff      call    nt!InterlockedPushEntrySList+0x79 (804e3a92)
    805755ad 33f6            xor     esi,esi
    原来8057559e就是NtOpenProcess函数所在的起始地址。  
    嗯,如果我们把8057559e改为指向我们函数的地址呢?比如 MyNtOpenProcess,那么系统就会直接调用MyNtOpenProcess,而不是原来的NtOpenProcess了。这就是SSDT HOOK 原理所在。

  3, ring0 inline hook
     ring0 inline hook 跟ring3的没什么区别了,如果硬说有的话,那么就是ring3发生什么差错的话程序会挂掉,ring0发生什么差错的话系统就挂掉,所以一定要很小心。inline hook的基本思想就是在目标函数中JMP到自己的监视函数,做一些判断然后再JMP回去。一般都是修改函数头,不过再其他地方JMP也是可以的。下面我们来点实际的吧:
     lkd> u nt!NtOpenProcess
     nt!NtOpenProcess:
     8057559e e95d6f4271      jmp     f199c500
     805755a3 e93f953978      jmp     f890eae7
     805755a8 e8e5e4f6ff      call    nt!InterlockedPushEntrySList+0x79 (804e3a92)
     ...
     同时打开“冰刃”跟“Rootkit Unhooker”我们就能在NtOpenProcess函数头看到这样的“奇观”,第一个jmp是“冰刃”的,第二个jmp是“Rootkit Unhooker”的。他们这样是防止被恶意程序通过TerminateProcess关闭。当然“冰刃”还Hook了NtTerminateProcess等函数。

×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
    好了,道理就说完了,下面就进入本文正题。
    对付ring0 inline hook的基本思路是这样的,自己写一个替换的内核函数,以NtOpenProcess为例,就是MyNtOpenProcess。然后修改SSDT表,让系统服务进入自己的函数MyNtOpenProcess。而MyNtOpenProcess要做的事就是,实现NtOpenProcess前10字节指令,然后再JMP到原来的NtOpenProcess的十字节后。这样NtOpenProcess函数头写的JMP都失效了,在ring3直接调用OpenProcess再也毫无影响。
***************************************************************************************************************************
#include<ntddk.h>

typedef struct _SERVICE_DESCRIPTOR_TABLE
{
        PVOID   ServiceTableBase;
        PULONG  ServiceCounterTableBase;
        ULONG   NumberOfService;
        ULONG   ParamTableBase;
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; //由于KeServiceDescriptorTable只有一项,这里就简单点了
extern PSERVICE_DESCRIPTOR_TABLE    KeServiceDescriptorTable;//KeServiceDescriptorTable为导出函数

/////////////////////////////////////
VOID Hook();
VOID Unhook();
VOID OnUnload(IN PDRIVER_OBJECT DriverObject);
//////////////////////////////////////
ULONG JmpAddress;//跳转到NtOpenProcess里的地址
ULONG OldServiceAddress;//原来NtOpenProcess的服务地址
//////////////////////////////////////
__declspec(naked) NTSTATUS __stdcall MyNtOpenProcess(PHANDLE ProcessHandle,
                                                   ACCESS_MASK DesiredAccess,
                                                   POBJECT_ATTRIBUTES ObjectAttributes,
                                                   PCLIENT_ID ClientId)
{
        DbgPrint("NtOpenProcess() called");
        __asm{
                push    0C4h
                push    804eb560h  //共十个字节
                jmp     [JmpAddress]                 
        }
}
///////////////////////////////////////////////////
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
        DriverObject->DriverUnload = OnUnload;
        DbgPrint("Unhooker load");
        Hook();
        return STATUS_SUCCESS;
}
/////////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
        DbgPrint("Unhooker unload!");
        Unhook();
}
/////////////////////////////////////////////////////
VOID Hook()
{
        ULONG  Address;
        Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//0x7A为NtOpenProcess服务ID
        DbgPrint("Address:0x%08X",Address);

        OldServiceAddress = *(ULONG*)Address;//保存原来NtOpenProcess的地址
        DbgPrint("OldServiceAddress:0x%08X",OldServiceAddress);

        DbgPrint("MyNtOpenProcess:0x%08X",MyNtOpenProcess);

        JmpAddress = (ULONG)NtOpenProcess + 10; //跳转到NtOpenProcess函数头+10的地方,这样在其前面写的JMP都失效了
        DbgPrint("JmpAddress:0x%08X",JmpAddress);
   
        __asm{//去掉内存保护
                cli
         mov  eax,cr0
                and  eax,not 10000h
                mov  cr0,eax
        }

        *((ULONG*)Address) = (ULONG)MyNtOpenProcess;//HOOK SSDT

        __asm{//恢复内存保护       
          mov  eax,cr0
                or   eax,10000h
                mov  cr0,eax
                sti
        }
}
//////////////////////////////////////////////////////
VOID Unhook()
{
        ULONG  Address;
        Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;//查找SSDT

        __asm{
                cli
          mov  eax,cr0
                and  eax,not 10000h
                mov  cr0,eax
        }

        *((ULONG*)Address) = (ULONG)OldServiceAddress;//还原SSDT

        __asm{       
         mov  eax,cr0
                or   eax,10000h
                mov  cr0,eax
                sti
        }

        DbgPrint("Unhook");
}
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
    就这么多了,或许有人说,没必要那么复杂,直接恢复NtOpenProcess不就行了吗?对于象“冰刃”“Rookit Unhooker”这些“善良”之辈的话是没问题的,但是象NP这些“穷凶极恶”之流的话,它会不断检测NtOpenProcess是不是已经被写回去,是的话,嘿嘿,机器马上重启。这也是这种方法的一点点妙用。

[公告]第4届看雪技术峰会2020年10月23日上海浦东喜来登由由大酒店召开!

收藏
点赞0
打赏
分享
最新回复 (186)
雪    币: 15494
活跃值: 活跃值 (2269)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
linhanshi 活跃值 2007-3-10 15:21
2
0
关注.
雪    币: 201
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
酷酷 活跃值 2007-3-10 15:24
3
0
顶,不得不顶的好贴
雪    币: 277
活跃值: 活跃值 (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
SkyJack 活跃值 2007-3-10 15:31
4
0
学习ing,狂顶一个.
雪    币: 349
活跃值: 活跃值 (1590)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 活跃值 8 2007-3-10 15:42
5
0
喜欢看堕落天才的文章,我想许多人很喜欢这方面的基础文章。
置顶2天鼓励一下。
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
古德里安 活跃值 2007-3-10 15:51
6
0
谁的帖子都可以不顶,堕落天才的帖子必须得顶
雪    币: 215
活跃值: 活跃值 (25)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
堕落天才 活跃值 10 2007-3-10 16:00
7
0
多谢看雪老大的鼓励
雪    币: 1
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DW_DLL 活跃值 2007-3-10 17:16
8
0
看来 "堕落天才" 必定成为 2007 年的黑马,顶起。

又见堕落天才
雪    币: 1829
活跃值: 活跃值 (15)
能力值: (RANK:330 )
在线值:
发帖
回帖
粉丝
Bughoho 活跃值 8 2007-3-10 17:51
9
0
我拱。。。

VC8

_disable();
uint64 cr0=__readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
雪    币: 302
活跃值: 活跃值 (13)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
wxxw 活跃值 6 2007-3-10 18:52
10
0
学习中.....
雪    币: 204
活跃值: 活跃值 (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
sbright 活跃值 2 2007-3-10 19:07
11
0
支持天才
雪    币: 200
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
苦茶 活跃值 2007-3-11 00:24
12
0
呵呵,不错,学习
雪    币: 208
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
我是雷锋 活跃值 2007-3-11 01:25
13
0
又见堕落天才!!!!!!!!!
雪    币: 100
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
小喂 活跃值 5 2007-3-11 10:00
14
0
强,支持一下!
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
girl 活跃值 1 2007-3-11 11:48
15
0
缺点3个

1,硬编码
    push    0C4h
    push    804eb560h

2,还原KeServiceDescriptorTable就无效

3,文章很简单,就是字多了点
雪    币: 204
活跃值: 活跃值 (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
sbright 活跃值 2 2007-3-11 12:37
16
0
支持girl发表缺点更少的文章..
雪    币: 519
活跃值: 活跃值 (47)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
笨笨雄 活跃值 14 2007-3-11 13:56
17
0
给系统打补丁,就恢复不了,除非内置各SP的SSDT
雪    币: 217
活跃值: 活跃值 (11)
能力值: ( LV13,RANK:530 )
在线值:
发帖
回帖
粉丝
foxabu 活跃值 13 2007-3-11 13:57
18
0
我觉得还是应该学习detour.lib那样HOOK 。自己准备一个小型反汇编器。 因为很显然搂主的代码不具有通用性~~
硬编码是很麻烦的东西~
雪    币: 519
活跃值: 活跃值 (47)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
笨笨雄 活跃值 14 2007-3-11 14:03
19
0
最初由 foxabu 发布
我觉得还是应该学习detour.lib那样HOOK 。自己准备一个小型反汇编器。 因为很显然搂主的代码不具有通用性~~
硬编码是很麻烦的东西~


直接读ntoskrnl.exe文件,取得函数的头10字节也是可以的吧?
雪    币: 226
活跃值: 活跃值 (10)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
heXer 活跃值 3 2007-3-11 16:12
20
0
最初由 笨笨雄 发布
直接读ntoskrnl.exe文件,取得函数的头10字节也是可以的吧?


怎么确保读取文件的内容是真实的呢?文件读取也可能被hook欺骗。
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
wangshq397 活跃值 8 2007-3-11 16:40
21
0
留名

.
雪    币: 25
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
xIkUg 活跃值 9 2007-3-11 17:09
22
0
最初由 heXer 发布
怎么确保读取文件的内容是真实的呢?文件读取也可能被hook欺骗。


很难保证内容的真实。。。到处都是陷阱
雪    币: 114
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
garasmc 活跃值 2007-3-11 18:26
23
0
强贴要留名
雪    币: 519
活跃值: 活跃值 (47)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
笨笨雄 活跃值 14 2007-3-11 18:51
24
0
突然发现我理解错foxabu的话了

我还得继续努力学习才能跟上大牛啊。。。
雪    币: 202
活跃值: 活跃值 (15)
能力值: ( LV9,RANK:1170 )
在线值:
发帖
回帖
粉丝
bxm 活跃值 29 2007-3-11 19:11
25
0
好东西,我顶.我支持!
雪    币: 1
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DW_DLL 活跃值 2007-3-11 19:39
26
0
最初由 foxabu 发布
我觉得还是应该学习detour.lib那样HOOK 。自己准备一个小型反汇编器。 因为很显然搂主的代码不具有通用性~~
硬编码是很麻烦的东西~


那你给大家共享一个,我们学习下。叫唤
雪    币: 278
活跃值: 活跃值 (15)
能力值: ( LV12,RANK:470 )
在线值:
发帖
回帖
粉丝
zhuwg 活跃值 11 2007-3-11 20:44
27
0
好东西,我顶
雪    币: 195
活跃值: 活跃值 (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
aki 活跃值 2 2007-3-11 20:45
28
0
直接hook 的那种,恢复起来好像比这个要稍微麻烦些
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
马本斋 活跃值 2007-3-11 21:11
29
0
受教拉
雪    币: 212
活跃值: 活跃值 (21)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
ppanger 活跃值 4 2007-3-11 21:43
30
0
LZ写得真的很棒,很多问题都说得深入浅出!希望LZ能多处这样的精品,也希望和LZ交朋友!  ppanger2006@163.com
雪    币: 246
活跃值: 活跃值 (11)
能力值: ( LV13,RANK:410 )
在线值:
发帖
回帖
粉丝
Isaiah 活跃值 10 2007-3-12 02:19
31
0
支持~~~~~~~
雪    币: 105
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
byjove 活跃值 2007-3-12 13:07
32
0
一定得支持一下,二下.

喜欢!
雪    币: 105
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
byjove 活跃值 2007-3-12 13:11
33
0
有一个小问题问下,如果从第10个字节开始不是完整的指令呢?
花指令那种.
雪    币: 295
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
firabc 活跃值 2007-3-12 18:42
34
0
最初由 xIkUg 发布
很难保证内容的真实。。。到处都是陷阱


同意,现在感觉是防不胜防呀
雪    币: 195
活跃值: 活跃值 (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
aki 活跃值 2 2007-3-12 18:57
35
0
如果在np之前载入驱动,好像还要解决很多问题
雪    币: 215
活跃值: 活跃值 (25)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
堕落天才 活跃值 10 2007-3-12 19:01
36
0
最初由 aki 发布
如果在np之前载入驱动,好像还要解决很多问题

要的!但本文不是专门破NP的,不过是说明一种方法。
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
仙剑太郎 活跃值 2 2007-3-12 19:46
37
0
强贴留名
雪    币: 351
活跃值: 活跃值 (120)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
笑熬浆糊 活跃值 2 2007-3-13 20:04
38
0
这个得顶~~~学习了
雪    币: 112
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
siaoxing 活跃值 1 2007-3-14 13:49
39
0
一个字好,先收藏,等我看得懂时再看
雪    币: 204
活跃值: 活跃值 (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
FetalError 活跃值 2007-3-14 16:25
40
0
实在的强贴,妙。
雪    币: 1
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DW_DLL 活跃值 2007-3-16 17:02
41
0
果然是这样,长见识了

nt!NtOpenProcess:
805c0e1e e97f4f0438      jmp     b8605da2
805c0e23 e9b92d4377      jmp     f79f3be1
805c0e28 e8d374f7ff      call    nt!wctomb+0x45 (80538300)

thank you so much!!!
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DazzleJ 活跃值 2007-3-16 18:06
42
0
堕落天才 果然后4字节名副其实!!!
雪    币: 206
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
linsion 活跃值 2007-3-16 22:41
43
0
楼主这段代码怎么编译才能成功呀?
雪    币: 39
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
busy 活跃值 2007-3-17 10:25
44
0
最初由 heXer 发布
怎么确保读取文件的内容是真实的呢?文件读取也可能被hook欺骗。

看到这个突然想,其实一切都可以被欺骗,我们的整个世界是不是一个其他生命的一个模拟场,或者这个世界都是虚拟的除了你自己是真实的,想想周围所有人都是虚拟的 太恐怖了
雪    币: 1
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DW_DLL 活跃值 2007-3-17 13:34
45
0
最初由 busy 发布
看到这个突然想,其实一切都可以被欺骗,我们的整个世界是不是一个其他生命的一个模拟场,或者这个世界都是虚拟的除了你自己是真实的,想想周围所有人都是虚拟的 太恐怖了


你最近没睡好??
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ossurrond 活跃值 2007-3-17 14:29
46
0
最初由 busy 发布
看到这个突然想,其实一切都可以被欺骗,我们的整个世界是不是一个其他生命的一个模拟场,或者这个世界都是虚拟的除了你自己是真实的,想想周围所有人都是虚拟的 太恐怖了


黑客帝国还没有醒。不过这个片子确实给人以深思。
雪    币: 626
活跃值: 活跃值 (14)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
rainbow 活跃值 1 2007-3-17 17:39
47
0
强贴留名
雪    币: 39
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
busy 活跃值 2007-3-17 19:50
48
0
最初由 ossurrond 发布
黑客帝国还没有醒。不过这个片子确实给人以深思。
其实不就是把人的所有神经系统HOOK一下就好了,那个不是科幻电影,离我们很近
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sdlqz 活跃值 2007-4-28 09:00
49
0
好帖,收藏!
雪    币: 214
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
yeihua 活跃值 2007-4-28 11:39
50
0
收藏之。。。。。。。
游客
登录 | 注册 方可回帖
返回