1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        技術(shù)版:文件被占用無法刪除怎么辦?

        共 13900字,需瀏覽 28分鐘

         ·

        2021-01-14 20:37

        技術(shù)版:文件被占用無法刪除怎么辦?了解一點(diǎn)操作系統(tǒng)知識(shí)的同學(xué)們應(yīng)該都知道,文件占用無法刪除,是因?yàn)槟承┻M(jìn)程正在使用該文件。

        要?jiǎng)h除這樣的文件,就需要讓那些進(jìn)程關(guān)閉文件,然后自然可以刪除。

        一句話的事,那究竟要怎么用代碼來實(shí)現(xiàn)這個(gè)功能呢?

        打開和關(guān)閉文件

        還記得上大學(xué)第一門語言課-C語言,迄今為止還依然活躍并被一直使用的語言。

        比匯編容易理解,又更接近底層,所以Windows操作系統(tǒng)內(nèi)核大部分代碼都是用C語言來編寫的。

        在C的課程里,我們學(xué)過通過FILE來操作使用文件,比如:

        FILE *fp;
        fp = fopen("c:\\temp\\test.txt", "r")

        通過讀的方式打開一個(gè)文件,使用非常簡單,后續(xù)通過fp這個(gè)結(jié)構(gòu)體指針操作文件即可。

        其實(shí)fopen并不接近操作系統(tǒng),他是對(duì)win32 API CreateFile的封裝。

        也就是前者是標(biāo)準(zhǔn)庫接口,在Windows、linux、unix等都是通用接口。

        而后者才是和操作系統(tǒng)關(guān)聯(lián)緊密,由微軟自己提供的API。

        要更好的理解進(jìn)程如何使用文件的,我們還得看看CreateFile這個(gè)API接口。

        HANDLE CreateFileA(
        LPCSTR lpFileName,
        DWORD dwDesiredAccess,
        DWORD dwShareMode,
        LPSECURITY_ATTRIBUTES lpSecurityAttributes,
        DWORD dwCreationDisposition,
        DWORD dwFlagsAndAttributes,
        HANDLE hTemplateFile
        );

        這是msdn對(duì)CreateFile的定義,簡單來看我們可以只關(guān)注lpFileName和返回值,lpFileName傳遞你要打開的文件,返回值是操作系統(tǒng)給你的一個(gè)代表文件的句柄(handle)。

        HANDLE hFile = CreateFileA("c:\\temp\\test.txt", ...);

        要對(duì)文件進(jìn)行讀、寫等操作都需要這個(gè)句柄,也就是說這個(gè)句柄至關(guān)重要,它表示文件正在被使用。

        然后什么時(shí)候結(jié)束使用呢,我們需要看另一個(gè)API CloseHandle.

        BOOL CloseHandle(
        HANDLE hObject
        );

        CloseHandle用于關(guān)閉一個(gè)正在被使用的文件,通過句柄來關(guān)閉。

        現(xiàn)在明白過來了嗎,只要我們讓進(jìn)程調(diào)用CloseHandle這個(gè)API,關(guān)閉被占用的文件句柄,那么該文件也就被解除占用了。

        哈哈,是不是很簡單。

        枚舉占用文件的進(jìn)程

        那么我就想問同學(xué)們一個(gè)問題,怎么知道哪些進(jìn)程在使用我們想刪除的文件呢?怎么去查找?

        帶著這個(gè)問題,我們繼續(xù)往下看。

        我們來想一個(gè)問題,操作系統(tǒng)給調(diào)用CreateFile的用戶返回了一個(gè)句柄,然后通過句柄來操作文件,那操作系統(tǒng)是如何知道句柄代表哪個(gè)文件呢?

        我們簡單思考一下,我們要達(dá)到這個(gè)目的有沒有什么方法,比如我用一個(gè)數(shù)組來存用戶打開的文件路徑,而數(shù)組序號(hào)就返回給用戶,下次用戶就只需要把序號(hào)給我,我就知道要操作什么問題了。

        演示代碼,忽略細(xì)節(jié)
        LPWSTR FileTable[100] = {0};
        HANDLE CreateFileA(
        LPCSTR lpFileName,
        ...)
        {
        for(int i = 0; i < 100; i ++) {
        if(FileTable[i] == NULL) { //還有空位
        FileTable[i] = lpFileName; //保存路徑
        return (HANDLE)i; //返回句柄
        }
        }
        return NULL;
        }
        BOOL CloseHandle(
        HANDLE hObject
        ) {
        if((int)hObject < 100) {
        if(FileTable[hObject]) {
        FileTable[hObject] = NULL;//找到文件路徑
        return TRUE;
        }
        }
        return FALSE;
        }

        上面簡單的代碼演示了一下我們粗略考略的文件和句柄的關(guān)系以及句柄的管理,那操作系統(tǒng)是不是這么做的呢?其實(shí)也差不多。

        任意進(jìn)程,只要每打開一個(gè)對(duì)象(包括文件、進(jìn)程、線程等等),就會(huì)獲得一個(gè)句柄。

        這個(gè)句柄用來標(biāo)志對(duì)某個(gè)對(duì)象的一次打開,通過句柄,可以直接找到對(duì)應(yīng)的內(nèi)核對(duì)象。

        每個(gè)進(jìn)程都有一個(gè)句柄表,用來記錄本進(jìn)程打開的所有內(nèi)核對(duì)象。

        句柄表可以簡單看做為一個(gè)一維數(shù)組,每個(gè)表項(xiàng)就是一個(gè)句柄,一個(gè)結(jié)構(gòu)體,一個(gè)句柄描述符。

         struct _HANDLE_TABLE_ENTRY  //句柄描述符
        struct _HANDLE_TABLE //句柄表描述符

        好,更加細(xì)節(jié)的句柄表的原理我們不用再深究,我們只需要知道每個(gè)進(jìn)程都有一個(gè)句柄表,通過句柄表就可以找到打開的文件。

        這就是我們的目的,我們需要查到進(jìn)程是不是打開了我們要?jiǎng)h除的文件,我們需要查句柄表。

        那怎么查呢?

        操作系統(tǒng)給用戶提供了一個(gè)接口ZwQuerySystemInformation。

        NTSTATUS WINAPI ZwQuerySystemInformation(
        _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
        _Inout_ PVOID SystemInformation,
        _In_ ULONG SystemInformationLength,
        _Out_opt_ PULONG ReturnLength
        );

        它可以獲取系統(tǒng)非常多的信息,包括進(jìn)程、模塊、處理器、內(nèi)存等等各種信息。

        而SystemHandleInformation = 16就能獲取到系統(tǒng)所有的句柄信息。

        typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
        {
        USHORT UniqueProcessId;//所屬進(jìn)程
        USHORT CreatorBackTraceIndex;
        UCHAR ObjectTypeIndex;
        UCHAR HandleAttributes;
        USHORT HandleValue; //句柄
        PVOID Object;
        ULONG GrantedAccess;
        } SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

        typedef struct _SYSTEM_HANDLE_INFORMATION
        {
        ULONG NumberOfHandles;
        SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
        } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

        既然知道了方法,下面就開始枚舉所有句柄,找到我們被占用的文件的進(jìn)程信息。

        Status = ZwQuerySystemInformation(SystemHandleInformation,
        Information,
        Length,
        &ReturnLength);

        for (i = 0; i < Information->NumberOfHandles; i++) {
        if (Information->Handles[i].UniqueProcessId != CurrentProcessId) {//不是當(dāng)前進(jìn)程
        Status = ZwQueryObject(TargetHandle, ObjectTypeInformation, &TypeInfo, sizeof(TypeInfo), NULL);
        RtlInitUnicodeString(&TargetType, L"File");
        if (!RtlEqualUnicodeString(&TypeInfo.Info.TypeName, &TargetType, FALSE)) {
        goto __next;
        }
        Status = ZwQueryObject(TargetHandle, ObjectNameInformation, &NameInfo, sizeof(NameInfo), NULL);
        if (RtlEqualUnicodeString(&NameInfo.Info.Name, &FileName, FALSE)) {
        printf("在進(jìn)程(%d)發(fā)現(xiàn)文件占用:(%x) %wZ\n",
        ProcessId,
        Information->Handles[i].HandleValue,
        &NameInfo.Info.Name);
        }
        }
        }

        ZwQuerySystemInformation獲取到所有句柄信息,通過循環(huán)枚舉Information->Handles,找到句柄類型屬于File,路徑是目標(biāo)文件的進(jìn)程。

        ZwQueryObject傳入ObjectTypeInformation可以獲取句柄類型,ZwQueryObject傳入ObjectNameInformation可以獲取文件路徑。

        如此兩個(gè)條件的對(duì)比,就能讓我們找到占用文件的進(jìn)程了。

        是不是感覺還挺簡單,不復(fù)雜嘛。

        坑一:ZwQueryObject

        前面提到,每個(gè)進(jìn)程都有自己的句柄表,所以ZwQuerySystemInformation枚舉拿到的句柄并不能直接使用,還需要復(fù)制一份到本進(jìn)程才有效。

        系統(tǒng)也提供了API叫做DuplicateHandle:

        BOOL DuplicateHandle(
        HANDLE hSourceProcessHandle,
        HANDLE hSourceHandle,
        HANDLE hTargetProcessHandle,
        LPHANDLE lpTargetHandle,
        DWORD dwDesiredAccess,
        BOOL bInheritHandle,
        DWORD dwOptions
        );

        DuplicateHandle(hSrcProc, Information->Handles[i].HandleValue, hCurProc, TargetHandle, ...);

        上面我們使用的TargetHandle就是通過復(fù)制獲取的。

        這個(gè)地方并不是坑,而是在通過ZwQueryObject獲取句柄對(duì)應(yīng)的文件路徑時(shí),會(huì)發(fā)生阻塞,導(dǎo)致程序卡死無法繼續(xù)運(yùn)行。

        0: kd> kv
        # ChildEBP RetAddr Args to Child
        00 d7fdb7cc 828aacda 00000000 00000000 a7d73040 nt!KiSwapContext+0x19 (FPO: [Uses EBP] [1,0,4])
        01 d7fdb86c 828aa358 d7fdb930 a7d73120 a7d73040 nt!KiSwapThread+0x4aa (FPO: [Non-Fpo])
        02 d7fdb8c8 828a9d67 00000000 00000000 00000000 nt!KiCommitThreadWait+0x128 (FPO: [Non-Fpo])
        03 d7fdb978 829298a3 8ff18afc 00000000 a7d73300 nt!KeWaitForSingleObject+0x1f7 (FPO: [Non-Fpo])
        04 d7fdb9a4 82c0759f 88c0e801 d7fdba18 8ff18ab0 nt!IopWaitForLockAlertable+0x3f (FPO: [Non-Fpo])
        05 d7fdb9cc 82d3f75c 88c0e800 a7d733f8 d7fdb9ef nt!IopWaitAndAcquireFileObjectLock+0x41 (FPO: [Non-Fpo])
        06 d7fdba1c 82bed31a 000001ee d7fdbb01 9a651dc0 nt!IopQueryXxxInformation+0x150f3e
        07 d7fdba9c 82becf65 00000000 007af7a4 00000210 nt!IopQueryNameInternal+0x31a (FPO: [Non-Fpo])
        08 d7fdbab8 82bece25 8ff18ab0 87ff2400 007af7a4 nt!IopQueryName+0x1b (FPO: [Non-Fpo])
        09 d7fdbb40 82bec6a6 00000210 d7fdbc04 d7fdbb01 nt!ObQueryNameStringMode+0x495 (FPO: [Non-Fpo])
        0a d7fdbbf8 829cce6b 8ff18ab0 00000000 007af7a4 nt!NtQueryObject+0x186 (FPO: [SEH])
        0b d7fdbbf8 77cd5ef0 8ff18ab0 00000000 007af7a4 nt!KiSystemServicePostCall (FPO: [0,3] TrapFrame @ d7fdbc14)

        經(jīng)過一些簡單的分析,如果文件被是同步(SYNCHRONIZE)打開的,內(nèi)核會(huì)等待一下鎖,等其他線程操作完成,本線程才能拿到所有權(quán)。

        //
        // Make a special check here to determine whether this is a synchronous
        // I/O operation. If it is, then wait here until the file is owned by
        // the current thread. If this is not a (serialized) synchronous I/O
        // operation, then initialize the local event.
        //

        if (FileObject->Flags & FO_SYNCHRONOUS_IO) {

        BOOLEAN interrupted;

        if (!IopAcquireFastLock( FileObject )) {
        status = IopAcquireFileObjectLock( FileObject,
        Mode,
        (BOOLEAN) ((FileObject->Flags & FO_ALERTABLE_IO) != 0),
        &interrupted );
        if (interrupted) {
        ObDereferenceObject( FileObject );
        return status;
        }
        }
        KeClearEvent( &FileObject->Event );
        synchronousIo = TRUE;
        }

        所以這里我們就需要通過線程和超時(shí)的方式來調(diào)用ZwQueryObject,讓程序可以不阻塞正常運(yùn)行。

        void
        QueryThread(
        IN PQUERY_CONTEXT Context
        ) {
        Status = ZwQueryObject(TargetHandle, ObjectNameInformation, &NameInfo, sizeof(NameInfo), NULL);
        }

        ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)QueryThread, Context, 0, NULL);
        Result = WaitForSingleObject(ThreadHandle, 1000); //等待1秒超時(shí),線程退出
        TerminateThread(ThreadHandle, 0);
        CloseHandle(ThreadHandle);

        坑二:文件Map

        解決上面的問題之后,我們基本就解決了文件占用的問題,大部分情況下,我們可以正常刪除文件了。

        可是…

        某些時(shí)候,我們要?jiǎng)h除的文件并不是普通文件,可能是一個(gè)DLL、或者其他特殊文件。

        關(guān)閉所有占用的句柄后,依然無法刪除文件,還是提示占用。

        這可怎么辦?

        類似于DLL這種文件,進(jìn)程在使用中,操作系統(tǒng)會(huì)映射一份內(nèi)存到進(jìn)程空間,此時(shí)并沒有句柄與之對(duì)應(yīng)。

        但是它卻關(guān)聯(lián)了文件的內(nèi)核對(duì)象,專業(yè)術(shù)語說增加了一次文件對(duì)象的引用。

        我們要知道,為了能夠安全刪除一個(gè)文件,操作系統(tǒng)需要保證該文件的內(nèi)核對(duì)象在兩種引用計(jì)數(shù)上清零。

        一個(gè)是句柄引用計(jì)數(shù),一個(gè)是對(duì)象引用計(jì)數(shù)。

        前面我們通過枚舉句柄,將句柄引用計(jì)數(shù)清零。

        但是因?yàn)楣蚕韮?nèi)存的原因,對(duì)象引用計(jì)數(shù)仍未清零,所以無法刪除文件。

        0: kd> !handle 48

        PROCESS fffffa801b7c6060
        SessionId: 1 Cid: 0b70 Peb: 7efdf000 ParentCid: 0588
        DirBase: 1bfea000 ObjectTable: fffff8a0029f27e0 HandleCount: 157.
        Image: procexp.exe

        Handle table at fffff8a0029f27e0 with 157 entries in use

        0004: Object: fffffa801bdcca10 GrantedAccess: 00000003 Entry: fffff8a0020cc010
        Object: fffffa801bdcca10 Type: (fffffa8018dcfa30) File
        ObjectHeader: fffffa801bdcc9e0 (new version)
        HandleCount: 0//句柄引用計(jì)數(shù) PointerCount: 1 //對(duì)象引用計(jì)數(shù)

        我們通過!vad倆看看內(nèi)存map。

        0: kd> !vad fffffa8019d34e00
        VAD Level Start End Commit
        fffffa8019d34e00 0 1000 12ce 0 Mapped READONLY \Windows\Globalization\Sorting\SortDefault.nls

        0: kd> dt _mmvad fffffa8019d34e00
        nt!_MMVAD
        +0x000 u1 :
        +0x008 LeftChild : (null)
        +0x010 RightChild : (null)
        +0x018 StartingVpn : 0x1000
        +0x020 EndingVpn : 0x12ce
        +0x028 u :
        +0x030 PushLock : _EX_PUSH_LOCK
        +0x038 u5 :
        +0x040 u2 :
        +0x048 Subsection : 0xfffffa80`1b56ef90 _SUBSECTION
        +0x048 MappedSubsection : 0xfffffa80`1b56ef90 _MSUBSECTION
        +0x050 FirstPrototypePte : 0xfffff8a0`00b02000 _MMPTE
        +0x058 LastContiguousPte : 0xfffff8a0`00b03670 _MMPTE
        +0x060 ViewLinks : _LIST_ENTRY [ 0xfffffa80`18ec81c0 - 0xfffffa80`18fcd190 ]
        +0x070 VadsProcess : 0xfffffa80`1b7c6061 _EPROCESS
        0: kd> dt 0xfffffa80`1b56ef90 _SUBSECTION
        nt!_SUBSECTION
        +0x000 ControlArea : 0xfffffa80`1b56ef10 _CONTROL_AREA
        +0x008 SubsectionBase : 0xfffff8a0`00b02000 _MMPTE
        +0x010 NextSubsection : 0xfffffa80`193a0a60 _SUBSECTION
        +0x018 PtesInSubsection : 0x2cf
        +0x020 UnusedPtes : 0
        +0x020 GlobalPerSessionHead : (null)
        +0x028 u :
        +0x02c StartingSector : 0
        +0x030 NumberOfFullSectors : 0x2cf
        0: kd> dt 0xfffffa80`1b56ef10 _CONTROL_AREA
        nt!_CONTROL_AREA
        +0x000 Segment : 0xfffff8a0`03b31fd0 _SEGMENT
        +0x008 DereferenceList : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
        +0x018 NumberOfSectionReferences : 0
        +0x020 NumberOfPfnReferences : 0x101
        +0x028 NumberOfMappedViews : 0x2a
        +0x030 NumberOfUserReferences : 0x2a
        +0x038 u :
        +0x03c FlushInProgressCount : 0
        +0x040 FilePointer : _EX_FAST_REF
        +0x048 ControlAreaLock : 0n0
        +0x04c ModifiedWriteCount : 0
        +0x04c StartingFrame : 0
        +0x050 WaitList : (null)
        +0x058 u2 :
        +0x068 LockedPages : 1
        +0x070 ViewList : _LIST_ENTRY [ 0xfffffa80`1be91570 - 0xfffffa80`1abbe690 ]
        0: kd> dx -id 0,0,fffffa801b7c6060 -r1 (*((ntkrnlmp!_EX_FAST_REF *)0xfffffa801b56ef50))
        (*((ntkrnlmp!_EX_FAST_REF *)0xfffffa801b56ef50)) [Type: _EX_FAST_REF]
        [+0x000] Object : 0xfffffa801b61fa14 [Type: void *]
        [+0x000 ( 3: 0)] RefCnt : 0x4 [Type: unsigned __int64]
        [+0x000] Value : 0xfffffa801b61fa14 [Type: unsigned __int64]
        0: kd> !object 0xfffffa801b61fa10
        Object: fffffa801b61fa10 Type: (fffffa8018dcfa30) File
        ObjectHeader: fffffa801b61f9e0 (new version)
        HandleCount: 0 PointerCount: 5
        Directory Object: 00000000 Name: \Windows\Globalization\Sorting\SortDefault.nls {HarddiskVolume2}

        0: kd> dt _file_object 0xfffffa801b61fa10
        nt!_FILE_OBJECT
        +0x000 Type : 0n5
        +0x002 Size : 0n216
        +0x008 DeviceObject : 0xfffffa80`19e9d530 _DEVICE_OBJECT
        +0x010 Vpb : 0xfffffa80`19eca270 _VPB
        +0x018 FsContext : 0xfffff8a0`00ad0140 Void
        +0x020 FsContext2 : 0xfffff8a0`00ad0330 Void
        +0x028 SectionObjectPointer : 0xfffffa80`1b61f808 _SECTION_OBJECT_POINTERS
        0: kd> dx -id 0,0,fffffa801b7c6060 -r1 ((ntkrnlmp!_SECTION_OBJECT_POINTERS *)0xfffffa801b61f808)
        ((ntkrnlmp!_SECTION_OBJECT_POINTERS *)0xfffffa801b61f808) : 0xfffffa801b61f808 [Type: _SECTION_OBJECT_POINTERS *]
        [+0x000] DataSectionObject : 0xfffffa801b56ef10 [Type: void *] //其實(shí)就是前面的_mmvad->Subsection->ControlArea
        [+0x008] SharedCacheMap : 0x0 [Type: void *]
        [+0x010] ImageSectionObject : 0x0 [Type: void *]

        SortDefault.nls是被映射到了進(jìn)程中,通過_mmvad->Subsection->ControlArea->FilePointer我們可以一步步定位到它引用的文件對(duì)象。

        !object 0xfffffa801b61fa10看到確實(shí)是該文件,也可以通過fileobject->SectionObjectPointer->DataSectionObject找到對(duì)應(yīng)的映射內(nèi)存。

        如此我們初步理解了文件map導(dǎo)致文件占用無法刪除文件的原理。

        下面我們就需要找到方法怎么解決這個(gè)問題。

        首先,需要枚舉進(jìn)程的虛擬內(nèi)存,找到是否有我們需要查找的文件的map,然后對(duì)該進(jìn)程有兩種操作:

        1. 非常暴力但是簡單的方法,那就是直接關(guān)閉進(jìn)程

        2. 或者unmap這塊內(nèi)存,解除對(duì)象引用計(jì)數(shù)(經(jīng)過測試,未成功)

        如何枚舉虛擬內(nèi)存呢,使用ZwQueryVirtualMemory.

        NTSTATUS ZwQueryVirtualMemory(
        _In_ HANDLE ProcessHandle,
        _In_opt_ PVOID BaseAddress,
        _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass,
        _Out_ PVOID MemoryInformation,
        _In_ SIZE_T MemoryInformationLength,
        _Out_opt_ PSIZE_T ReturnLength
        );

        //MemoryBasicInformation
        typedef struct _MEMORY_BASIC_INFORMATION {
        PVOID BaseAddress;
        PVOID AllocationBase;
        ULONG AllocationProtect;
        USHORT PartitionId;
        SIZE_T RegionSize;
        ULONG State;
        ULONG Protect;
        ULONG Type;
        } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

        Type
        The type of pages in the region. The following types are defined.
        MEM_IMAGE 0x1000000 Indicates that the memory pages within the region are mapped into the view of an image section.
        MEM_MAPPED 0x40000 Indicates that the memory pages within the region are mapped into the view of a section.
        MEM_PRIVATE 0x20000 Indicates that the memory pages within the region are private (that is, not shared by other processes).

        從0地址開始,每次加一個(gè)頁,獲取內(nèi)存信息,如果內(nèi)存的type是MEM_IMAGE或者M(jìn)EM_MAPPED,那么就是文件map,然后獲取虛擬內(nèi)存對(duì)應(yīng)名字,判斷是不是目標(biāo)文件。

        for (;;) {
        Status = ZwQueryVirtualMemory(ProcessHandle, BaseAddress, MemoryBasicInformation,
        &MemoryInfo, sizeof(MemoryInfo), NULL);
        if (MemoryInfo.Type == MEM_IMAGE || //image
        MemoryInfo.Type == MEM_MAPPED) { //data
        Status = ZwQueryVirtualMemory(ProcessHandle, BaseAddress, MemoryMappedFilenameInformation, &Name, sizeof(Name), NULL);
        if (RtlEqualUnicodeString(&Name.u, &TargetName, TRUE)) {
        //找到目標(biāo)文件
        break;
        }
        }
        }
        }

        找到目標(biāo)進(jìn)程后,關(guān)閉進(jìn)程,輕松刪除文件。

        (完)

        如果覺得內(nèi)容還不錯(cuò),請(qǐng)不吝點(diǎn)贊或在看,謝謝。

        歡迎關(guān)注公眾號(hào):Anhkgg日記本

        瀏覽 74
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产1级片| 能看黄色电影的网站 | 人人妻人人澡人人爽久久av | 一级生性活片免费视频在线观看 | 少妇李芬┅┅快┅┅用力 | 欧美视频专线一二三区 | 国产女十八毛片水真多 | 一本色道加勒比精品一区二区 | 蜜臀av性久久久久蜜臀av麻豆 | 91成人无码视频 |