9

我正在尝试使用 DLL 注入方法编写显示另一个程序使用的位图的应用程序,并且我想获取它正在使用的 GDI 句柄的这个特定进程列表(类似于 GDIView.exe 实用程序中的列表)。我找到了有关 NtQuerySystemInformation 函数的文章,但此描述仅适用于内核对象的句柄。有人可以帮忙吗?

4

3 回答 3

16

这是一个控制台应用程序代码,它转储给定进程标识符的所有 GDI 句柄。它应该为 32 位或 64 位应用程序以及在 64 位操作系统上运行的 32 位应用程序编译和工作正常。它使用了很多未记录的函数,所以不要依赖它 :-) 共享 GDI 表的结构归功于冯源的原创作品。(我不得不为 WOW64 改编它)。

以下是在记事本(64 位)进程上运行时的示例输出:

[C:\Temp\EnumGdi\Debug]EnumGdi.exe 5916
DC handle:0xF20105DB
Bitmap handle:0xDF05118B
Font handle:0xDC0A19E0
Font handle:0xAB0A1A62
DC handle:0xA3011A63
Region handle:0xAF041B7B
Brush handle:0x11101C5B
Font handle:0x280A1CA1
Font handle:0xBB0A1D13
Bitmap handle:0xA3051DD8
Font handle:0xB40A1DDC
Region handle:0x3A041EE4
Brush handle:0x0B101F04
Region handle:0xC6041F3D
Font handle:0x2C0A2384
Brush handle:0xBA1024DA

枚举Gdi.cpp:

#include "stdafx.h"
#include "enumgdi.h"

int _tmain(int argc, _TCHAR* argv[])
{   
    if (argc < 2)
    {
        printf("Format is EnumGdi <process id>\n");
        return 0;
    }

    // get process identifier
    DWORD dwId = _wtoi(argv[1]);

    // open the process
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwId);
    DWORD err = 0;
    if (hProcess == NULL)
    {
        printf("OpenProcess %u failed\n", dwId);
        err = GetLastError();
        return -1;
    }

    // determine if 64 or 32-bit processor
    SYSTEM_INFO si;
    GetNativeSystemInfo(&si);

    // NOTE: as this is undocumented, it *may vary* depending on bitness (32/64) and on Windows version.
    // use WinDbg "dt ntdll!_PEB" command and search for GdiSharedHandleTable offset to find the truth out
    DWORD GdiSharedHandleTableOffset  = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0xF8 : 0x94;
    DWORD tableCount = 16384; // count of GDI table cells

    // determine if this process is running on WOW64
    BOOL wow;
    IsWow64Process(GetCurrentProcess(), &wow);

    // read basic info to get PEB address, we only need the beginning of PEB
    DWORD pebSize = GdiSharedHandleTableOffset + 8;
    LPBYTE peb = (LPBYTE)malloc(pebSize);
    ZeroMemory(peb, pebSize);

    if (wow)
    {
        // we're running as a 32-bit process in a 64-bit process
        PROCESS_BASIC_INFORMATION_WOW64 pbi;
        ZeroMemory(&pbi, sizeof(pbi));

        // get process information from 64-bit world
        _NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64QueryInformationProcess64");
        err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
        if (err != 0)
        {
            printf("NtWow64QueryInformationProcess64 failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read PEB from 64-bit address space
        _NtWow64ReadVirtualMemory64 read = (_NtWow64ReadVirtualMemory64)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64ReadVirtualMemory64");
        err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
        if (err != 0)
        {
            printf("NtWow64ReadVirtualMemory64 PEB failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // get GDI table ptr from PEB
        GDICELL_WOW64* gdiTable = (GDICELL_WOW64*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
        if (gdiTable == NULL)
        {
            printf("GDI32.DLL is not loaded in the process\n");
            CloseHandle(hProcess);
            return -1;
        }
        free(peb);
        DWORD tableSize = sizeof(GDICELL_WOW64) * tableCount; // size of GDI table
        GDICELL_WOW64* table = (GDICELL_WOW64*)malloc(tableSize); // local table copied over to our address space

        // copy GDI table
        err = read(hProcess, gdiTable, table, tableSize, NULL);
        if (err != 0)
        {
            printf("NtWow64ReadVirtualMemory64 GdiTable failed\n");
            free(table);
            CloseHandle(hProcess);
            return -1;
        }

        for(DWORD i = 0; i < tableCount; i++)
        {
            GDICELL_WOW64 cell = table[i];
            if (cell.wProcessId != dwId)
                continue;

            HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
            WORD type = cell.wType & 0x7F;
            switch(type)
            {
                case 1:
                    printf("DC handle:0x%08X\n", gdiHandle );
                    break;

                case 4:
                    printf("Region handle:0x%08X\n", gdiHandle);
                    break;

                case 5:
                    printf("Bitmap handle:0x%08X\n", gdiHandle);
                    break;

                case 8:
                    printf("Palette handle:0x%08X\n", gdiHandle);
                    break;

                case 10:
                    printf("Font handle:0x%08X\n", gdiHandle);
                    break;

                case 16:
                    printf("Brush handle:0x%08X\n", gdiHandle);
                    break;

                case 48:
                    printf("Pen handle:0x%08X\n", gdiHandle);
                    break;

                default:
                    printf("Unknown type handle:0x%08X\n", gdiHandle);
                    break;
            }
        }
        free(table);
    }
    else
    {
        // we're running as a 32-bit process in a 32-bit OS, or as a 64-bit process in a 64-bit OS
        PROCESS_BASIC_INFORMATION pbi;
        ZeroMemory(&pbi, sizeof(pbi));

        // get process information
        _NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
        err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
        if (err != 0)
        {
            printf("NtQueryInformationProcess failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read PEB
        _NtReadVirtualMemory read = (_NtReadVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtReadVirtualMemory");
        err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
        if (err != 0)
        {
            printf("NtReadVirtualMemory PEB failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // get GDI table ptr
        GDICELL* gdiTable = (GDICELL*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
        if (gdiTable == NULL)
        {
            printf("GDI32.DLL is not loaded in the process\n");
            CloseHandle(hProcess);
            return -1;
        }
        free(peb);

        DWORD tableSize = sizeof(GDICELL) * tableCount; // size of GDI table
        GDICELL* table = (GDICELL*)malloc(tableSize); // local table copied over to our address space

        // read GDI table
        err = read(hProcess, gdiTable, table, tableSize, NULL);
        if (err != 0)
        {
            printf("NtReadVirtualMemory GdiTable failed\n");
            free(table);
            CloseHandle(hProcess);
            return -1;
        }

        for(DWORD i = 0; i < tableCount; i++)
        {
            GDICELL cell = table[i];
            if (cell.wProcessId != dwId)
                continue;

            HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
            WORD type = cell.wType & 0x7F;
            switch(type)
            {
                case 1:
                    printf("DC handle:0x%08X\n", gdiHandle );
                    break;

                case 4:
                    printf("Region handle:0x%08X\n", gdiHandle);
                    break;

                case 5:
                    printf("Bitmap handle:0x%08X\n", gdiHandle);
                    break;

                case 8:
                    printf("Palette handle:0x%08X\n", gdiHandle);
                    break;

                case 10:
                    printf("Font handle:0x%08X\n", gdiHandle);
                    break;

                case 16:
                    printf("Brush handle:0x%08X\n", gdiHandle);
                    break;

                case 48:
                    printf("Pen handle:0x%08X\n", gdiHandle);
                    break;

                default:
                    printf("Unknown type handle:0x%08X\n", gdiHandle);
                    break;
            }
        }
        free(table);
    }
    CloseHandle(hProcess);
}

枚举Gdi.h:

#pragma once
#include "stdafx.h"

// defines a GDI CELL
typedef struct
{
    LPVOID pKernelAddress;
    USHORT wProcessId;
    USHORT wCount;
    USHORT wUpper;
    USHORT wType;
    LPVOID pUserAddress;
} GDICELL;

// defines a GDI CELL for WOW64
typedef struct
{
    PVOID64 pKernelAddress;
    USHORT wProcessId;
    USHORT wCount;
    USHORT wUpper;
    USHORT wType;
    PVOID64 pUserAddress;
} GDICELL_WOW64;

// NtQueryInformationProcess for pure 32 and 64-bit processes
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
    IN HANDLE ProcessHandle,
    ULONG ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    );

typedef NTSTATUS (NTAPI *_NtReadVirtualMemory)(
    IN HANDLE ProcessHandle,
    IN PVOID BaseAddress,
    OUT PVOID Buffer,
    IN SIZE_T Size,
    OUT PSIZE_T NumberOfBytesRead);

// NtQueryInformationProcess for 32-bit process on WOW64
typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)(
    IN HANDLE ProcessHandle,
    IN PVOID64 BaseAddress,
    OUT PVOID Buffer,
    IN ULONG64 Size,
    OUT PULONG64 NumberOfBytesRead);

// PROCESS_BASIC_INFORMATION for pure 32 and 64-bit processes
typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PVOID PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;

// PROCESS_BASIC_INFORMATION for 32-bit process on WOW64
// The definition is quite funky, as we just lazily doubled sizes to match offsets...
typedef struct _PROCESS_BASIC_INFORMATION_WOW64 {
    PVOID Reserved1[2];
    PVOID64 PebBaseAddress;
    PVOID Reserved2[4];
    ULONG_PTR UniqueProcessId[2];
    PVOID Reserved3[2];
} PROCESS_BASIC_INFORMATION_WOW64;
于 2012-12-23T15:56:36.117 回答
4

首先,您必须定义“GDI 句柄”的确切含义以及为什么需要了解它们,因为有不同类型的句柄。

从技术上讲,有 3 种主要类型的手柄:

  • 内核句柄。示例:文件句柄、同步对象(事件、互斥锁等)、文件映射等。
  • 用户句柄。示例:HWND, HDC, HICON, 桌面手柄等。
  • GDI 句柄。示例:HBITMAP, HGDIOBJ(子类型是HRGN,HPEN等等)。

特别是有些人混淆了用户和 GDI 句柄。不是每个人都知道HDC用于绘图的实际上不是GDI 句柄。

从实现的角度来看,用户句柄和 GDI 句柄之间存在很大差异。用户句柄是系统范围的,它们在内核中进行管理。因此有机会在内核模式下收集有关它们的所有信息。OTOH GDI 句柄是特定于进程的。一些 GDI 对象纯粹在用户模式下进行管理(例如区域和 DIB)。

于 2012-12-16T21:42:40.633 回答
1
Compilable and Buildable
//Lets name it GDIInquiry.cpp
    #pragma once
    #include "stdafx.h"
    #include "StdAfx.h"
    #include <tchar.h>
    #include <stdio.h>
    #include <malloc.h>
    #include <dbghelp.h>
    #include <shlwapi.h>
    #include <ShlObj.h>
    #include "GDIInquiry.h"

    int _tmain(int argc, _TCHAR* argv[])
    {
        if (argc < 2)
        {
            printf("Format is EnumGdi <process id>\n");
            system("pause");
            return 0;
        }

        // get process identifier
        DWORD dwId = _wtoi(argv[1]);

        // open the process
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwId);
        DWORD err = 0;
        if (hProcess == NULL)
        {
            printf("OpenProcess %u failed\n", dwId);
            err = GetLastError();
            system("pause");
            return -1;
        }

        // determine if 64 or 32-bit processor
        SYSTEM_INFO si;
        GetNativeSystemInfo(&si);

        // NOTE: as this is undocumented, it *may vary* depending on bitness (32/64) and on Windows version.
        // use WinDbg "dt ntdll!_PEB" command and search for GdiSharedHandleTable offset to find the truth out
        DWORD GdiSharedHandleTableOffset = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0xF8 : 0x94;
        DWORD tableCount = 16384; // count of GDI table cells

        // determine if this process is running on WOW64
        BOOL wow;
        IsWow64Process(GetCurrentProcess(), &wow);

        // read basic info to get PEB address, we only need the beginning of PEB
        DWORD pebSize = GdiSharedHandleTableOffset + 8;
        LPBYTE peb = (LPBYTE)malloc(pebSize);
        ZeroMemory(peb, pebSize);

        int nDCHandle, nRegionHandle, nBitmapHandle, nPaletteHandle, nFontHandle, nPenHandle, nBrushHandle, nOtherHandle;
        nDCHandle = nRegionHandle = nBitmapHandle = nPaletteHandle = nFontHandle = nPenHandle = nBrushHandle = nOtherHandle = 0;

        if (wow)
        {
            // we're running as a 32-bit process in a 64-bit process
            PROCESS_BASIC_INFORMATION_WOW64 pbi;
            ZeroMemory(&pbi, sizeof(pbi));

            // get process information from 64-bit world
            _NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64QueryInformationProcess64");
            err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
            if (err != 0)
            {
                printf("NtWow64QueryInformationProcess64 failed\n");
                CloseHandle(hProcess);
                system("pause");
                return -1;
            }

            // read PEB from 64-bit address space
            _NtWow64ReadVirtualMemory64 read = (_NtWow64ReadVirtualMemory64)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64ReadVirtualMemory64");
            err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
            if (err != 0)
            {
                printf("NtWow64ReadVirtualMemory64 PEB failed\n");
                CloseHandle(hProcess);
                system("pause");
                return -1;
            }

            // get GDI table ptr from PEB
            GDICELL_WOW64* gdiTable = (GDICELL_WOW64*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
            if (gdiTable == NULL)
            {
                printf("GDI32.DLL is not loaded in the process\n");
                CloseHandle(hProcess);
                system("pause");
                return -1;
            }
            free(peb);
            DWORD tableSize = sizeof(GDICELL_WOW64)* tableCount; // size of GDI table
            GDICELL_WOW64* table = (GDICELL_WOW64*)malloc(tableSize); // local table copied over to our address space

            // copy GDI table
            err = read(hProcess, gdiTable, table, tableSize, NULL);
            if (err != 0)
            {
                printf("NtWow64ReadVirtualMemory64 GdiTable failed\n");
                free(table);
                CloseHandle(hProcess);
                system("pause");
                return -1;
            }

            for (DWORD i = 0; i < tableCount; i++)
            {
                GDICELL_WOW64 cell = table[i];
                if (cell.wProcessId != dwId)
                    continue;

                HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
                WORD type = cell.wType & 0x7F;
                switch (type)
                {
                case 1:
                    //printf("DC handle:0x%08X\n", gdiHandle);
                    nDCHandle++;
                    break;

                case 4:
                    //printf("Region handle:0x%08X\n", gdiHandle);
                    nRegionHandle++;
                    break;

                case 5:
                    //printf("Bitmap handle:0x%08X\n", gdiHandle);
                    nBitmapHandle++;
                    break;

                case 8:
                    //printf("Palette handle:0x%08X\n", gdiHandle);
                    nPaletteHandle++;
                    break;

                case 10:
                    //printf("Font handle:0x%08X\n", gdiHandle);
                    nFontHandle++;
                    break;

                case 16:
                    //printf("Brush handle:0x%08X\n", gdiHandle);
                    nPenHandle++;
                    break;

                case 48:
                    //printf("Pen handle:0x%08X\n", gdiHandle);
                    nBrushHandle++;
                    break;

                default:
                    //printf("Unknown type handle:0x%08X\n", gdiHandle);
                    nOtherHandle++;
                    break;
                }
            }
            free(table);
        }
        else
        {
            // we're running as a 32-bit process in a 32-bit OS, or as a 64-bit process in a 64-bit OS
            PROCESS_BASIC_INFORMATION pbi;
            ZeroMemory(&pbi, sizeof(pbi));

            // get process information
            _NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
            err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
            if (err != 0)
            {
                printf("NtQueryInformationProcess failed\n");
                CloseHandle(hProcess);
                system("pause");
                return -1;
            }

            // read PEB
            _NtReadVirtualMemory read = (_NtReadVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtReadVirtualMemory");
            err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
            if (err != 0)
            {
                printf("NtReadVirtualMemory PEB failed\n");
                CloseHandle(hProcess);
                system("pause");
                return -1;
            }

            // get GDI table ptr
            GDICELL* gdiTable = (GDICELL*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
            if (gdiTable == NULL)
            {
                printf("GDI32.DLL is not loaded in the process\n");
                CloseHandle(hProcess);
                system("pause");
                return -1;
            }
            free(peb);

            DWORD tableSize = sizeof(GDICELL)* tableCount; // size of GDI table
            GDICELL* table = (GDICELL*)malloc(tableSize); // local table copied over to our address space

            // read GDI table
            err = read(hProcess, gdiTable, table, tableSize, NULL);
            if (err != 0)
            {
                printf("NtReadVirtualMemory GdiTable failed\n");
                free(table);
                CloseHandle(hProcess);
                system("pause");
                return -1;
            }

            for (DWORD i = 0; i < tableCount; i++)
            {
                GDICELL cell = table[i];
                if (cell.wProcessId != dwId)
                    continue;

                HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
                WORD type = cell.wType & 0x7F;
                switch (type)
                {
                case 1:
                    //printf("DC handle:0x%08X\n", gdiHandle);
                    nDCHandle++;
                    break;

                case 4:
                    //printf("Region handle:0x%08X\n", gdiHandle);
                    nRegionHandle++;
                    break;

                case 5:
                    //printf("Bitmap handle:0x%08X\n", gdiHandle);
                    nBitmapHandle++;
                    break;

                case 8:
                    //printf("Palette handle:0x%08X\n", gdiHandle);
                    nPaletteHandle++;
                    break;

                case 10:
                    //printf("Font handle:0x%08X\n", gdiHandle);
                    nFontHandle++;
                    break;

                case 16:
                    //printf("Brush handle:0x%08X\n", gdiHandle);
                    nPenHandle++;
                    break;

                case 48:
                    //printf("Pen handle:0x%08X\n", gdiHandle);
                    nBrushHandle++;
                    break;

                default:
                    //printf("Unknown type handle:0x%08X\n", gdiHandle);
                    nOtherHandle++;
                    break;
                }
            }
            free(table);
        }
        CloseHandle(hProcess);
        int nTotalGDI = nDCHandle + nRegionHandle + nBitmapHandle + nPaletteHandle + nFontHandle + nPenHandle + nBrushHandle + nOtherHandle;
        printf("Bitmap:%d\n", nBitmapHandle);
        printf("Brush:%d\n", nPenHandle);
        printf("DeviceContext:%d\n", nDCHandle);
        printf("Font:%d\n", nFontHandle);
        printf("Palette:%d\n", nPaletteHandle);
        printf("Pen:%d\n", nBrushHandle);
        printf("Region:\%d\n", nRegionHandle);
        printf("Unknown:%d\n", nOtherHandle);
        printf("GDITotal:%d\n", nTotalGDI);
        return 1;
    }

和 GDIInquiry.h 代码如下

#pragma once
#include "stdafx.h"
#include "Winternl.h"
// defines a GDI CELL
typedef struct
{
    LPVOID pKernelAddress;
    USHORT wProcessId;
    USHORT wCount;
    USHORT wUpper;
    USHORT wType;
    LPVOID pUserAddress;
} GDICELL;

// defines a GDI CELL for WOW64
typedef struct
{
    PVOID64 pKernelAddress;
    USHORT wProcessId;
    USHORT wCount;
    USHORT wUpper;
    USHORT wType;
    PVOID64 pUserAddress;
} GDICELL_WOW64;

// NtQueryInformationProcess for pure 32 and 64-bit processes
typedef NTSTATUS(NTAPI *_NtQueryInformationProcess)(
    IN HANDLE ProcessHandle,
    ULONG ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    );

typedef NTSTATUS(NTAPI *_NtReadVirtualMemory)(
    IN HANDLE ProcessHandle,
    IN PVOID BaseAddress,
    OUT PVOID Buffer,
    IN SIZE_T Size,
    OUT PSIZE_T NumberOfBytesRead);

// NtQueryInformationProcess for 32-bit process on WOW64
typedef NTSTATUS(NTAPI *_NtWow64ReadVirtualMemory64)(
    IN HANDLE ProcessHandle,
    IN PVOID64 BaseAddress,
    OUT PVOID Buffer,
    IN ULONG64 Size,
    OUT PULONG64 NumberOfBytesRead);

// PROCESS_BASIC_INFORMATION for pure 32 and 64-bit processes
/*typedef struct
{
PVOID Reserved1;
PVOID PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
}PROCESS_BASIC_INFORMATION;*/

// PROCESS_BASIC_INFORMATION for 32-bit process on WOW64
// The definition is quite funky, as we just lazily doubled sizes to match offsets...
typedef struct PROCESS_BASIC_INFORMATION_WOW64
{
    PVOID Reserved1[2];
    PVOID64 PebBaseAddress;
    PVOID Reserved2[4];
    ULONG_PTR UniqueProcessId[2];
    PVOID Reserved3[2];
}PROCESS_BASIC_INFORMATION_WOW64;

结束

Run commands:
..\..\GDIInquiry\Debug GDIInquiry.exe PID

Sample Output:
cmd
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

..\..\GDIInquiry\Debug>GDIInquiry.exe 6772
Bitmap:302
Brush:139
DeviceContext:133
Font:75
Palette:1
Pen:0
Region:11
Unknown:0
GDITotal:661
于 2015-10-13T13:16:13.070 回答