首页
社区
课程
招聘
[原创]rootkit 直接访问硬件之[一]
发表于: 2008-3-27 15:14 39305

[原创]rootkit 直接访问硬件之[一]

2008-3-27 15:14
39305

今天我们一起来学习下如何实现ring3程序直接访问硬件。这部分内容跟windows保护模式中的I/O保护部分有直接的关系。
rootkit直接访问硬件准备写三篇,今天是开篇。后续请大家继续跟贴。高手飘过,呵呵。
我们来看看Windows系统I/O访问保护的方法:
80386采用I/O特权级IPOL和I/O许可位图的方法来控制输入/输出,实现输入/输出保护.
I/O许可位图位于任务状态段TSS中。I/O特权级IPOL就是EFLAGS寄存器中IOPL位。

采用的保护规则是:
(1)若CPL<=IOPL,则直接转步骤(8);
(2)取得I/O位图开始偏移;
(3)计算I/O地址对应位所在字节在I/O许可位图内的偏移;
(4)计算位偏移以形成屏蔽码值,即计算I/O地址对应位在字节中的第几位;
(5)把字节偏移加上位图开始偏移,再加1,所得值与TSS界限比较,若越界,则产生出错码为0的通用保护故障;
(6)若不越界,则从位图中读对应字节及下一个字节;
(7)把读出的两个字节与屏蔽码进行与运算,若结果不为0表示检查未通过,则产生出错码为0的通用保护故障;
(8)进行I/O访问。
其中windows中IOPL值是为0,从第一条规则我们可以了解到为什么ring0下可以直接访问I/O了,因为ring0中CPL = 0 ,完全满足第一条。而ring3下CPL = 3 ,第一条不能满足。
我们知道每个任务使用各自的EFLAGS值和拥有自己的TSS,所以每个任务可以有不同的IOPL。所以想在ring3下访问I/O,最简单的方法当然是修改当前进程IOPL的值为3了。
根据前面了解到IPOL就是EFLAGS寄存器中IOPL位,所以我们尝试下修改eflags寄存器看看。如图:

我们写段代码看看:
#include "stdio.h"
int main(int argc, char* argv[])
{
        _asm
        {
                pushfd
                pop eax   
                or eax,0x3000  //设置iopl = 3
                push eax
                popfd
                pushfd   //看看修改后的效果
                pop eax  //eflags寄存器的值保存到eax中
        }
        return 0;
}
通过上面代码,我们发现不能直接修改eflags中iopl的值。呵呵,继续想办法吧。
每个Windows进程都有一个相对应的执行体进程(EPROCESS),EPROCESS不仅包括了进程的许多属性,还包扩了许多指向其他数据结构的指针,其中包含了大量有用的信息.
为了找出思路,我们还是先看看进程eprocess吧。
lkd> dt _eprocess
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER
   +0x078 ExitTime         : _LARGE_INTEGER
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY
   +0x090 QuotaUsage       : [3] Uint4B
   +0x09c QuotaPeak        : [3] Uint4B
   +0x0a8 CommitCharge     : Uint4B
   +0x0ac PeakVirtualSize  : Uint4B
   +0x0b0 VirtualSize      : Uint4B
   +0x0b4 SessionProcessLinks : _LIST_ENTRY
   +0x0bc DebugPort        : Ptr32 Void
   +0x0c0 ExceptionPort    : Ptr32 Void
   +0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE
   +0x0c8 Token            : _EX_FAST_REF
   +0x0cc WorkingSetLock   : _FAST_MUTEX
   +0x0ec WorkingSetPage   : Uint4B
   +0x0f0 AddressCreationLock : _FAST_MUTEX
   +0x110 HyperSpaceLock   : Uint4B
   +0x114 ForkInProgress   : Ptr32 _ETHREAD
   +0x118 HardwareTrigger  : Uint4B
   +0x11c VadRoot          : Ptr32 Void
   +0x120 VadHint          : Ptr32 Void
   +0x124 CloneRoot        : Ptr32 Void
   +0x128 NumberOfPrivatePages : Uint4B
   +0x12c NumberOfLockedPages : Uint4B
   +0x130 Win32Process     : Ptr32 Void
   +0x134 Job              : Ptr32 _EJOB
   +0x138 SectionObject    : Ptr32 Void
   +0x13c SectionBaseAddress : Ptr32 Void
   +0x140 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK
   +0x144 WorkingSetWatch  : Ptr32 _PAGEFAULT_HISTORY
   +0x148 Win32WindowStation : Ptr32 Void
   +0x14c InheritedFromUniqueProcessId : Ptr32 Void
   +0x150 LdtInformation   : Ptr32 Void
   +0x154 VadFreeHint      : Ptr32 Void
   +0x158 VdmObjects       : Ptr32 Void
   +0x15c DeviceMap        : Ptr32 Void
   +0x160 PhysicalVadList  : _LIST_ENTRY
   +0x168 PageDirectoryPte : _HARDWARE_PTE_X86
   +0x168 Filler           : Uint8B
   +0x170 Session          : Ptr32 Void
   +0x174 ImageFileName    : [16] UChar
   +0x184 JobLinks         : _LIST_ENTRY
   +0x18c LockedPagesList  : Ptr32 Void
   +0x190 ThreadListHead   : _LIST_ENTRY
   +0x198 SecurityPort     : Ptr32 Void
   +0x19c PaeTop           : Ptr32 Void
   +0x1a0 ActiveThreads    : Uint4B
   +0x1a4 GrantedAccess    : Uint4B
   +0x1a8 DefaultHardErrorProcessing : Uint4B
   +0x1ac LastThreadExitStatus : Int4B
   +0x1b0 Peb              : Ptr32 _PEB
   +0x1b4 PrefetchTrace    : _EX_FAST_REF
   +0x1b8 ReadOperationCount : _LARGE_INTEGER
   +0x1c0 WriteOperationCount : _LARGE_INTEGER
   +0x1c8 OtherOperationCount : _LARGE_INTEGER
   +0x1d0 ReadTransferCount : _LARGE_INTEGER
   +0x1d8 WriteTransferCount : _LARGE_INTEGER
   +0x1e0 OtherTransferCount : _LARGE_INTEGER
   +0x1e8 CommitChargeLimit : Uint4B
   +0x1ec CommitChargePeak : Uint4B
   +0x1f0 AweInfo          : Ptr32 Void
   +0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   +0x1f8 Vm               : _MMSUPPORT
   +0x238 LastFaultCount   : Uint4B
   +0x23c ModifiedPageCount : Uint4B
   +0x240 NumberOfVads     : Uint4B
   +0x244 JobStatus        : Uint4B
   +0x248 Flags            : Uint4B
   +0x248 CreateReported   : Pos 0, 1 Bit
   +0x248 NoDebugInherit   : Pos 1, 1 Bit
   +0x248 ProcessExiting   : Pos 2, 1 Bit
   +0x248 ProcessDelete    : Pos 3, 1 Bit
   +0x248 Wow64SplitPages  : Pos 4, 1 Bit
   +0x248 VmDeleted        : Pos 5, 1 Bit
   +0x248 OutswapEnabled   : Pos 6, 1 Bit
   +0x248 Outswapped       : Pos 7, 1 Bit
   +0x248 ForkFailed       : Pos 8, 1 Bit
   +0x248 HasPhysicalVad   : Pos 9, 1 Bit
   +0x248 AddressSpaceInitialized : Pos 10, 2 Bits
   +0x248 SetTimerResolution : Pos 12, 1 Bit
   +0x248 BreakOnTermination : Pos 13, 1 Bit
   +0x248 SessionCreationUnderway : Pos 14, 1 Bit
   +0x248 WriteWatch       : Pos 15, 1 Bit
   +0x248 ProcessInSession : Pos 16, 1 Bit
   +0x248 OverrideAddressSpace : Pos 17, 1 Bit
   +0x248 HasAddressSpace  : Pos 18, 1 Bit
   +0x248 LaunchPrefetched : Pos 19, 1 Bit
   +0x248 InjectInpageErrors : Pos 20, 1 Bit
   +0x248 VmTopDown        : Pos 21, 1 Bit
   +0x248 Unused3          : Pos 22, 1 Bit
   +0x248 Unused4          : Pos 23, 1 Bit
   +0x248 VdmAllowed       : Pos 24, 1 Bit
   +0x248 Unused           : Pos 25, 5 Bits
   +0x248 Unused1          : Pos 30, 1 Bit
   +0x248 Unused2          : Pos 31, 1 Bit
   +0x24c ExitStatus       : Int4B
   +0x250 NextPageColor    : Uint2B
   +0x252 SubSystemMinorVersion : UChar
   +0x253 SubSystemMajorVersion : UChar
   +0x252 SubSystemVersion : Uint2B
   +0x254 PriorityClass    : UChar
   +0x255 WorkingSetAcquiredUnsafe : UChar
   +0x258 Cookie           : Uint4B
大致上看了下,没有iopl相关的项,我们继续看看里面的子项内容。
看看 +0x000 Pcb  : _KPROCESS这个结构体。
lkd> dt _kprocess
ntdll!_KPROCESS
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 ProfileListHead  : _LIST_ENTRY
   +0x018 DirectoryTableBase : [2] Uint4B
   +0x020 LdtDescriptor    : _KGDTENTRY
   +0x028 Int21Descriptor  : _KIDTENTRY
   +0x030 IopmOffset       : Uint2B
   +0x032 Iopl             : UChar
   +0x033 Unused           : UChar
   +0x034 ActiveProcessors : Uint4B
   +0x038 KernelTime       : Uint4B
   +0x03c UserTime         : Uint4B
   +0x040 ReadyListHead    : _LIST_ENTRY
   +0x048 SwapListEntry    : _SINGLE_LIST_ENTRY
   +0x04c VdmTrapcHandler  : Ptr32 Void
   +0x050 ThreadListHead   : _LIST_ENTRY
   +0x058 ProcessLock      : Uint4B
   +0x05c Affinity         : Uint4B
   +0x060 StackCount       : Uint2B
   +0x062 BasePriority     : Char
   +0x063 ThreadQuantum    : Char
   +0x064 AutoAlignment    : UChar
   +0x065 State            : UChar
   +0x066 ThreadSeed       : UChar
   +0x067 DisableBoost     : UChar
   +0x068 PowerState       : UChar
   +0x069 DisableQuantum   : UChar
   +0x06a IdealNode        : UChar
   +0x06b Flags            : _KEXECUTE_OPTIONS
   +0x06b ExecuteOptions   : UChar
在这里,我们找到了Iopl和IopmOffset 这两项。其中IopmOffset指该进程I/O许可位图在tss区域的偏移,也就是TSS中的IoMapBase值。而Iopl是一个布尔值,该值决定了进程中线程切换时,EFLAGS寄存器中IOPL位是设置为0还是为3。 如果进程Iopl值为TRUE,
则EFLAGS寄存器中IOPL值为3,如果进程Iopl值为FALSE,则EFLAGS寄存器中IOPL值为0.
到这里我们更进了一步。呵呵,离成功不远了。

接下来我们发现在ntdll.dll中有一个导出函数NtSetInformationProcess。
我们可以在ring3下直接调用NtSetInformationProcess函数的ProcessUserModeIOPL功能将进程中Iopl值为TRUE。但是有个前提,需要当前用户具有SeTcbPrivilege这个权限才行,并且当前用户必须是administrator.

SeTcbPrivilege表示当前用户的操作代表了系统的操作。并且这个权限只有system有,Administrator都没有这个权限。因此,我们需要首先得到这个权限,并且将这个权限使能。然后再调用NtSetInformationProcess的ProcessUserModeIOPL功能。思路很清晰了,这个我们在ring3下就能实现。贴代码:
#include <string.h>
#include <windows.h>
#include <tchar.h>
#include <ntsecapi.h>
#include <process.h>

BOOL EnablePrivilege(PTCHAR Privilege)
{

    BOOL rc = FALSE;
    HANDLE hToken;
    LUID luid;
    TOKEN_PRIVILEGES tokenPrivilege;
        DWORD dwProcID = GetCurrentProcessId();
        HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcID);
    //
    //  Open the current process' token.
    //
    rc = OpenProcessToken(
            GetCurrentProcess(),
            TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
            &hToken);

    if (rc)
    {
        rc = LookupPrivilegeValue(NULL, Privilege, &luid);
        if (rc)
        {
            tokenPrivilege.PrivilegeCount = 1;
            tokenPrivilege.Privileges[0].Luid = luid;
            tokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            //
            //  Assign the given privilege.
            //
            rc = AdjustTokenPrivileges(
                    hToken,
                    FALSE,
                    &tokenPrivilege,
                    sizeof(tokenPrivilege),
                    NULL,
                    NULL);
        }

    }
    if (hToken)
    {
        CloseHandle(hToken);
    }
   
        if(hProc)
                CloseHandle(hProc);

    return rc;

}

BOOL EnableProcPrivilege()
{
        BOOL rc = TRUE;
        LSA_HANDLE PolicyHandle;
        PSID Sid=0;
        DWORD cbSid=0;
        LPTSTR ReferencedDomainName=0;
        DWORD cbReferencedDomainName=0;
        SID_NAME_USE peUse;
        PUNICODE_STRING UserRights=0; //UnicodeString Pointer to PRIVILEGE
        ULONG Count=0; //
        HANDLE token=0;
        PTOKEN_PRIVILEGES TokenInformation=0;
        BOOL owned=0;
        OSVERSIONINFO osv;
        char username[30];
        DWORD cb = 30;
        LSA_OBJECT_ATTRIBUTES ObjectAttributes;
        ZeroMemory(&ObjectAttributes,sizeof(ObjectAttributes));
        ZeroMemory(&osv,sizeof(osv));
        osv.dwOSVersionInfoSize = sizeof(osv);
        //
        //判断当前系统是否为nt及以上
        //
        GetVersionEx(&osv);
        if(!(osv.dwPlatformId & VER_PLATFORM_WIN32_NT))
        {
            rc = FALSE;
        }
        //
        // 判断当前用户是否为administrator
        //
        GetUserName(username,&cb);
        if(stricmp(username,"administrator"))
        {
        rc = FALSE;
        }

        //
        //First open LSA policy database
        //the call returns a NTSTATUS. NTSTATUS 0 means everything is OK.
        //
        if (LsaOpenPolicy(
                                        0,
                                        &ObjectAttributes,
                                        GENERIC_EXECUTE|GENERIC_READ|GENERIC_WRITE,
                                        &PolicyHandle
                                        ))
        {
                rc = FALSE;
        }

        Sid=new char[500];
        ReferencedDomainName=new CHAR[100];
        cbSid=500;
        cbReferencedDomainName=100;

        //
        //Show Administrator SID
        //
        if (!LookupAccountName(
                                                        0,
                                                        "Administrator",
                                                        Sid,
                                                        &cbSid,
                                                        ReferencedDomainName,
                                                        &cbReferencedDomainName,
                                                        &peUse
                                                        ))
        {
            rc =  FALSE;
        }

        //
        //Add SeTchPrivilege to Administrator if not owned yet!
        //
        UserRights=new LSA_UNICODE_STRING;
        UserRights->Buffer=L"SeTcbPrivilege";
        UserRights->MaximumLength=28;
        UserRights->Length=28;

        if (LsaAddAccountRights(
                                                        PolicyHandle,
                                                        Sid,
                                                        UserRights,
                                                        1
                                                        ))
        {
                rc = FALSE;
        }

        if(Sid)
                delete Sid;
        if(ReferencedDomainName)
                delete ReferencedDomainName;
        if (UserRights)
                delete UserRights;
        if (TokenInformation)
                delete TokenInformation;
        if (token)
                CloseHandle(token);
        if (PolicyHandle)
                LsaClose(PolicyHandle);

        rc = EnablePrivilege(SE_TCB_NAME);
        return rc;

}

BOOL EnableUserModeHardwareIO()
{

    typedef ULONG (__stdcall* pfn_ZwSetInformationProcess)(
                                                                                                                        HANDLE,
                                                                                                                        ULONG,
                                                                                                                        PVOID,
                                                                                                                        ULONG);

    BOOL rc = FALSE;
    HMODULE hNtDll = NULL;
    ULONG IOPL = 1;
    INT ProcessUserModeIOPL = 16;
    pfn_ZwSetInformationProcess ZwSetInformationProcess;
    DWORD dwProcessID = GetCurrentProcessId();
    HANDLE   hProc= OpenProcess(   PROCESS_ALL_ACCESS,   TRUE,dwProcessID);
    hNtDll = GetModuleHandle("ntdll.dll");
    if (hNtDll)
    {
        ZwSetInformationProcess = (pfn_ZwSetInformationProcess)
                                  GetProcAddress(hNtDll, "ZwSetInformationProcess");

        if (ZwSetInformationProcess)
        {
            //
            //  Enable SeTcbPrivilege
            //
            rc = EnableProcPrivilege();
            if (rc)
            {
                //
                //  Grant user mode hardware IO access.
                //                            
                rc = ZwSetInformationProcess(
                        hProc,
                        ProcessUserModeIOPL,
                        &IOPL,
                        sizeof(IOPL));           
                //
                //  An NTSTATUS is returned, so zero is success.
                //
                if (!rc)
                {
                    rc = TRUE;
                }
            }
        }
    }
    CloseHandle(hProc);
    return rc;
}

int __cdecl main(int argc, char* argv[])
{
    if (EnableUserModeHardwareIO())
    {
        //
        //  直接给键盘发一个指令,让机器重启
        //
        __asm mov dx, 0x64
        __asm mov al, 0xFE
        __asm out dx, al
    }
    return 0;
}

毕竟ring3下访问还是比较费劲,后面附上一个ring3通过驱动访问硬件的例子。由于驱动加载部分代码网上很多,就不再罗嗦了。
注:本机环境 winxp sp2.


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (29)
雪    币: 272
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
沙发    !!!
2008-3-27 15:22
0
雪    币: 272
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
RING3就能实现啊???

呵呵,主动防御又要开始更新了。。。。

新一轮的恶性病毒也即将出笼。。。。。。。。
2008-3-27 15:30
0
雪    币: 145
活跃值: (85)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
顶。。。。。。。。。正适合我们的新手学习。收藏,。
2008-3-27 17:36
0
雪    币: 1979
活跃值: (1120)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
学习 收藏了!
2008-3-27 17:52
0
雪    币: 189
活跃值: (56)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
6
又见楼主好文
btw:请楼主抽时间帮我看看这个帖子好吗?等了2天了
http://bbs.pediy.com/showthread.php?t=61926
谢谢谢谢
2008-3-27 18:05
0
雪    币: 287
活跃值: (102)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
7
强大加 膜拜
2008-3-27 18:15
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
我印象中LsaAddAccountRights()添加权限需要重新启动才能发挥作用,难道我记错了?如果要重新认识启动才能发挥作用,那么上面那些代码使用性就打折扣了。
2008-3-27 19:32
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
9
WSSRUN就行了~
2008-3-28 01:45
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
10
wssrun在我的机器加载进程,直接操作硬件没有测试通过。本机环境:winxpsp2
具体做法是:自己生成一个exe.然后用wssrun运行我的exe文件。exe的代码如下:
#include "stdio.h"

int main(int argc, char* argv[])
{
        _asm
        {
            mov dx,0x64
                  mov al,0xfe
                  out dx,al
        }
        return 0;
}
2008-3-28 09:02
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
在我的电脑上运行出错
上传的附件:
2008-3-28 10:53
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
12
ring3程序需要用Administrator用户登陆。再试试看?
我本机测试通过,我的环境是xp sp2.
2008-3-28 12:52
0
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
很好,学习学习
2008-3-28 13:01
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
RtlInitUnicodeString(&devLinkUnicd,L"\\??\\DIRECTHADRWARE");
  这句有点不懂。。\??是什么意思..   符号连接??
为什么有些代码里面好像不用这个,,  应该是什么时候用或者说怎么用最好呢??
实在是不懂 网上也没搜到相关的资料   谢谢
2008-3-28 17:00
0
雪    币: 451
活跃值: (78)
能力值: ( LV12,RANK:470 )
在线值:
发帖
回帖
粉丝
15
这个没什么。就是设备名初始化而已
保证你的代码里面一致就ok
2008-3-28 18:15
0
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
16
一般都是#define 一下再用的。

呵呵~
2008-3-28 19:12
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
谢谢大虾们。。。。
2008-3-28 20:02
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
测试了一下,工作不错.

测试平台: xpsp2 w2k3 sp1, xpsp3

注意问题:
(1)直接运行一次后必须重起才能起作用.原因楼 上说了.
(2) 使用 wssrun可以避免重新启动的,在XPSP3测过.难道ms不知道吗....

贴上基于楼主的代码和wssrun(网上的), 用winddk环境编译就可.
上传的附件:
2008-4-24 13:29
0
雪    币: 299
活跃值: (25)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
19
Negative,这种生僻的方法反而好防,正常软件有几个会调用ZwSetInformationProcess来设置自己的IO许可位图?
2008-4-26 11:16
0
雪    币: 215
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
你防也没用,我学壳把函数搬到程序空间来执行........................
2008-4-27 08:42
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
不行.我的电脑上 xp sp2 上会报错.无论是用LZ的还是用 real.zip 中的都会报错
2008-12-19 15:38
0
雪    币: 256
活跃值: (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
22
强啊,学习下
2008-12-26 18:38
0
雪    币: 34
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
楼主,我试了代码,OUT 端口时还是报了特权指令的错误啊,请问是什么问题,多谢
2009-8-12 16:36
0
雪    币: 411
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
学习了
2010-9-29 02:10
0
雪    币: 29
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
经典文章,mark一下
2011-7-25 12:57
0
游客
登录 | 注册 方可回帖
返回
//