• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            道。道。道

            安全特性不等于安全的特性

               :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理

            常用鏈接

            搜索

            •  

            最新評論

              攔截api的技術有很多種,大體分為用戶層和內(nèi)核層的攔截.這里只說說用戶層的攔截.而用戶層也分為許多種:修改PE文件導入表,直接修改要攔截的api的內(nèi)存(從開始到最后,使程序跳轉到指定的地址執(zhí)行).不過大部分原理都是修改程序流程,使之跳轉到你要執(zhí)行的地方,然后再返回到原地址.原來api的功能必須還能實現(xiàn).否則攔截就失去作用了.修改文件導入表的方法的缺點是如果用戶程序動態(tài)加載(使用LoadLibrary和GetProcAddress函數(shù)),攔截將變得復雜一些.所以這里介紹一下第二種方法,直接修改api,當然不是全局的.(后面會說到)

              需要了解的一些知識:

              1.windows內(nèi)存的結構屬性和進程地址空間

              2.函數(shù)堆棧的一些知識



            一:win2000和xp的內(nèi)存結構和進程地址空間

            ??windows采用4GB平坦虛擬地址空間的做法。即每個進程單獨擁有4GB的地址空間。每個進程只能訪問自己的這4GB的虛擬空間,而對于其他進程的地址空間則是不可見的。這樣保證了進程的安全性和穩(wěn)定性。但是,這4GB的空間是一個虛擬空間,在使用之前,我們必須先保留一段虛擬地址,然后再為這段虛擬地址提交物理存儲器。可是我們的內(nèi)存大部分都還沒有1GB,那么這4GB的地址空間是如何實現(xiàn)的呢?事實上windows采用的內(nèi)存映射這種方法,即把物理磁盤當作內(nèi)存來使用,比如我們打開一個可執(zhí)行文件的時候,操作系統(tǒng)會為我們開辟這個4GB的地址空間:0x00000000--0xffffffff。其中0x00000000--0x7fffffff是屬于用戶層的空間.0x80000000--0xffffffff則屬于共享內(nèi)核方式分區(qū),主要是操作系統(tǒng)的線程調(diào)度,內(nèi)存管理,文件系統(tǒng)支持,網(wǎng)絡支持和所有設備驅動程序。對于用戶層的進程,這些地址空間是不可訪問的。任何訪問都將導致一個錯誤。開辟這4GB的虛擬地址空間之后,系統(tǒng)會把磁盤上的執(zhí)行文件映射到進程的地址空間中去(一般是在地址0x00400000,可以通過修改編譯選項來修改這個地址)而一個進程運行所需要的動態(tài)庫文件則一般從0x10000000開始加載。但是如果所有的動態(tài)庫都加載到這個位置肯定會引起沖突。因此必須對一些可能引起沖突的dll編譯時重新修改基地址。但是對于所有的操作系統(tǒng)所提供的動態(tài)庫windows已經(jīng)定義好了映射在指定的位置。這個位置會隨著版本的不同而會有所改變,不過對于同一臺機器上的映射地址來說都是一樣的。即在a進程里映射的kernel32.dll的地址和在進程b里的kernel32.dll的地址是一樣的。對于文件映射是一種特殊的方式,使得程序不需要進行磁盤i/o就能對磁盤文件進行操作,而且支持多種保護屬性。對于一個被映射的文件,主要是使用CreateFileMapping函數(shù),利用他我們可以設定一些讀寫屬性:PAGE_READONLY,PAGE_READWRITE,PAGE_WRITECOPY.第一參數(shù)指定只能對該映射文件進行讀操作。任何寫操作將導致內(nèi)存訪問錯誤。第二個參數(shù)則指明可以對映射文件進行讀寫。這時候,任何對文件的讀寫都是直接操作文件的。而對于第三個參數(shù)PAGE_WRITECOPY顧名思義就是寫入時拷貝,任何向這段內(nèi)存寫入的操作(因為文件是映射到進程地址空間的,對這段空間的讀寫就相當于對文件進行的直接讀寫)都將被系統(tǒng)捕獲,并重新在你的虛擬地址空間重新保留并分配一段內(nèi)存,你所寫入的一切東西都將在這里,而且你原先的指向映射文件的內(nèi)存地址也會實際指向這段重新分配的內(nèi)存,于是在進程結束后,映射文件內(nèi)容并沒有改變,只是在運行期間在那段私有拷貝的內(nèi)存里面存在著你修改的內(nèi)容。windows進程運行所需要映射的一些系統(tǒng)dll就是以這種方式映射的,比如常用的ntdll.dll,kernel32.dll,gdi32.dll.幾乎所有的進程都會加載這三個動態(tài)庫。如果你在一個進程里修改這個映射文件的內(nèi)容,并不會影響到其他的進程使用他們。你所修改的只是在本進程的地址空間之內(nèi)的。事實上原始文件并沒有被改變。
            這樣,在后面的修改系統(tǒng)api的時候,實際就是修改這些動態(tài)庫地址內(nèi)的內(nèi)容。前面說到這不是修改全局api就是這個原因,因為他們都是以寫入時拷貝的方式來映射的。不過這已經(jīng)足夠了,windows提供了2個強大的內(nèi)存操作函數(shù)ReadProcessMemory和WriteProcessMemory.利用這兩個函數(shù)我們就可以隨便對任意進程的任意用戶地址空間進行讀寫了。但是,現(xiàn)在有一個問題,我們該寫什么,說了半天,怎么實現(xiàn)跳轉呢?現(xiàn)在來看一個簡單的例子:
            MessageBox(NULL, "World", "Hello", 0);
            我們在執(zhí)行這條語句的時候,調(diào)用了系統(tǒng)api MessageBox,實際上在程序中我沒有定義UNICODE宏,系統(tǒng)調(diào)用的是MessageBox的ANSI版本MessageBoxA,這個函數(shù)是由user32.dll導出的。下面是執(zhí)行這條語句的匯編代碼:
            0040102A?? push???????? 0
            0040102C?? push???????? offset string "Hello" (0041f024)
            00401031?? push???????? offset string "World" (0041f01c)
            00401036?? push???????? 0
            00401038?? call???????? dword ptr [__imp__MessageBoxA@16 (0042428c)]
            前面四條指令分別為參數(shù)壓棧,因為MessageBoxA是__stdcall調(diào)用約定,所以參數(shù)是從右往左壓棧的。最后再CALL 0x0042428c

            看看0042428c這段內(nèi)存的值:
            0042428C?? 0B 05 D5 77 00 00 00
            可以看到這個值0x77d5050b,正是user32.dll導出函數(shù)MessageBoxA的入口地址。

            這是0x77D5050B處的內(nèi)容,??
            77D5050B 8B FF???????????????? mov???????? edi,edi
            77D5050D 55?????????????????? push???????? ebp
            77D5050E 8B EC???????????????? mov???????? ebp,esp
            理論上只要改變api入口和出口的任何機器碼,都可以攔截該api。這里我選擇最簡單的修改方法,直接修改api入口的前十個字節(jié)來實現(xiàn)跳轉。為什么是十字節(jié)呢?其實修改多少字節(jié)都沒有關系,只要實現(xiàn)了函數(shù)的跳轉之后,你能把他們恢復并讓他繼續(xù)運行才是最重要的。在CPU的指令里,有幾條指令可以改變程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。這里我選擇CALL指令,因為他是以函數(shù)調(diào)用的方式來實現(xiàn)跳轉的,這樣可以帶一些你需要的參數(shù)。到這里,我該說說函數(shù)的堆棧了。

            總結:windows進程所需要的動態(tài)庫文件都是以寫入時拷貝的方式映射到進程地址空間中的。這樣,我們只能攔截指定的進程。修改目標進程地址空間中的指定api的入口和出口地址之間的任意數(shù)據(jù),使之跳轉到我們的攔截代碼中去,然后再恢復這些字節(jié),使之能順利工作。




            二:函數(shù)堆棧的一些知識

            ??正如前面所看到MessageBoxA函數(shù)執(zhí)行之前的匯編代碼,首先將四個參數(shù)壓棧,然后CALL MessageBoxA,這時候我們的線程堆棧看起來應該是這樣的:

            |???? |???? <---ESP
            |返回地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |..?? |

            我們再看MessageBoxA的匯編代碼,
            77D5050B 8B FF???????????????? mov???????? edi,edi
            77D5050D 55?????????????????? push???????? ebp
            77D5050E 8B EC???????????????? mov???????? ebp,esp
            注意到堆棧的操作有PUSH ebp,這是保存當前的基址指針,以便一會兒恢復堆棧后返回調(diào)用線程時使用,然后再有mov ebp,esp就是把當前esp的值賦給ebp,這時候我們就可以使用 ebp+偏移 來表示堆棧中的數(shù)據(jù),比如參數(shù)1就可以表示成[ebp+8],返回地址就可以表示成[ebp+4]..如果我們在攔截的時候要對這些參數(shù)和返回地址做任何處理,就可以使用這種方法。如果這個時候函數(shù)有局部變量的話,就通過減小ESP的值的方式來為之分配空間。接下來就是保存一些寄存器:EDI,ESI,EBX.要注意的是,函數(shù)堆棧是反方向生長的。這時候堆棧的樣子:
            |....|
            |EDI|?? <---ESP
            |ESI|
            |EBX|
            |局部變量|
            |EBP???? |??
            |返回地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |..?? |

            在函數(shù)返回的時候,由函數(shù)自身來進行堆棧的清理,這時候清理的順序和開始入棧的順序恰恰相反,類似的匯編代碼可能是這樣的:

            pop edi
            pop esi
            pop ebx
            add esp, 4
            pop ebp
            ret 0010
            先恢復那些寄存器的值,然后通過增加ESP的值的方式來釋放局部變量。這里可以用mov esp, ebp來實現(xiàn)清空所有局部變量和其他一些空閑分配空間。接著函數(shù)會恢復EBP的值,利用指令POP EBP來恢復該寄存器的值。接著函數(shù)運行ret 0010這個指令。該指令的意思是,函數(shù)把控制權交給當前棧頂?shù)牡刂返闹噶睿瑫r清理堆棧的16字節(jié)的參數(shù)。如果函數(shù)有返回值的話,那在EAX寄存器中保存著當前函數(shù)的返回值。如果是__cdecl調(diào)用方式,則執(zhí)行ret指令,對于堆棧參數(shù)的處理交給調(diào)用線程去做。如wsprintf函數(shù)。

            這個時候堆棧又恢復了原來的樣子。線程得以繼續(xù)往下執(zhí)行...
            在攔截api的過程之中一個重要的任務就是保證堆棧的正確性。你要理清每一步堆棧中發(fā)生了什么。



            三:形成思路
            ??
            ??呵呵,不知道你現(xiàn)在腦海是不是有什么想法。怎么去實現(xiàn)攔截一個api?
            ??這里給出一個思路,事實上攔截的方法真的很多,理清了一個,其他的也就容易了。而且上面所說的2個關鍵知識,也可以以另外的形式來利用。
            ??我以攔截CreateFile這個api為例子來簡單說下這個思路吧:
            ??
            ??首先,既然我們要攔截這個api就應該知道這個函數(shù)在內(nèi)存中的位置吧,至少需要知道從哪兒入口。CreateFile這個函數(shù)是由kernel32.dll這個動態(tài)庫導出的。我們可以使用下面的方法來獲取他映射到內(nèi)存中的地址:
            ??HMODULE hkernel32 = LoadLibrary("Kernel32.dll");
            ??PVOID dwCreateFile = GetProcAddress(hkernei32, "CreateFileA");
            這就可以得到createfile的地址了,注意這里是獲取的createfile的ansic版本。對于UNICODE版本的則獲取CreateFileW。這時dwCreateFile的值就是他的地址了。對于其他進程中的createfile函數(shù)也是這個地址,前面說過windows指定了他提供的所有的dll文件的加載地址。
            ??
            ??接下來,我們該想辦法實現(xiàn)跳轉了。最簡單的方法就是修改這個api入口處的代碼了。但是我們該修改多少呢?修改的內(nèi)容為什么呢?前面說過我們可以使用CALL的方式來實現(xiàn)跳轉,這種方法的好處是可以為你的攔截函數(shù)提供一個或者多個參數(shù)。這里只要一個參數(shù)就足夠了。帶參數(shù)的函數(shù)調(diào)用的匯編代碼是什么樣子呢,前面也已經(jīng)說了,類似與調(diào)用MessageBoxA時的代碼:

            PUSH?? 參數(shù)地址
            CALL?? 函數(shù)入口地址(這里為一個偏移地址)

            執(zhí)行這2條指令就能跳轉到你要攔截的函數(shù)了,但是我們該修改成什么呢。首先,我們需要知道這2條指令的長度和具體的機器代碼的值。其中PUSH對應0x68,而CALL指令對應的機器碼為0xE8,而后面的則分別對應攔截函數(shù)的參數(shù)地址和函數(shù)的地址。注意第一個是一個直接的地址,而第二個則是一個相對地址。當然你也可以使用0xFF0x15這個CALL指令來進行直接地址的跳轉。
            下面就是計算這2個地址的值了,
            對于參數(shù)和函數(shù)體的地址,要分情況而定,對于對本進程中api的攔截,則直接取地址就可以了。對于參數(shù),可以先定義一個參數(shù)變量,然后取變量地址就ok了。
            如果是想攔截其他進程中的api,則必須使用其他一些方法,最典型的方法是利用VirtualAllocEx函數(shù)來在其他進程中申請和提交內(nèi)存空間。然后用WriteProcessMemory來分別把函數(shù)體和參數(shù)分別寫入申請和分配的內(nèi)存空間中去。然后再生成要修改的數(shù)據(jù),最后用WriteProcessMemory來修改api入口,把入口的前10字節(jié)修改為剛剛生成的跳轉數(shù)據(jù)。比如在遠程進程中你寫入的參數(shù)和函數(shù)體的內(nèi)存地址分別為0x00010000和0x00011000,則生成的跳轉數(shù)據(jù)為 68 00 00 01 00 E8 00 10 01 00(PUSH 00010000?? CALL 00011000),這樣程序運行createfile函數(shù)的時候將會先運行PUSH 00010000?? CALL 00011000,這樣就達到了跳轉的目的。此刻我們應該時刻注意堆棧的狀態(tài),對于CreateFile有
            HANDLE CreateFile(
            LPCTSTR lpFileName,
            DWORD dwDesiredAccess,
            DWORD dwShareMode,
            LPSECURITY_ATTRIBUTES lpSecurityAttributes,
            DWORD dwCreationDisposition,
            DWORD dwFlagsAndAttributes,
            HANDLE hTemplateFile
            );
            可以看到其有7個參數(shù),于是在調(diào)用之前,堆棧應該已經(jīng)被壓入了這7個參數(shù),堆棧的樣子:
            |....|???? <---ESP
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            這是執(zhí)行到我們的跳轉語句:PUSH 00010000,于是堆棧又變了:

            |....|???? <---ESP
            |00010000|
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            接著執(zhí)行CALL 00011000,堆棧變?yōu)椋?
            |...|?? <---ESP
            |api入口之后的第11個字節(jié)的指令的地址|????
            |00010000|
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            接下來就到了我們的攔截函數(shù)中拉,當然,函數(shù)肯定也會做一些類似動作,把EBP壓棧,為局部變量分配空間等。這時候堆棧的樣子又變了:

            |EDI|?? <---ESP
            |ESI|
            |EBX|
            |局部變量|
            |EBP|???? <---EBP
            |api入口之后的第11個字節(jié)的指令的地址|????
            |00010000|
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            這時候,你想做什么就盡情地做吧,獲取參數(shù)信息,延緩執(zhí)行CreateFile函數(shù)等等。拿獲取打開文件句柄的名字來說吧,文件名是第一個參數(shù),前面說過我們可以用[EBP+8]來獲取參數(shù),但是對照上面的堆棧形狀,中間又加了另外一些數(shù)據(jù),所以我們用[EBP+16]來獲取第一個參數(shù)的地址。比如:
            char* PFileName = NULL;
            __asm{
            MOV EAX,[EBP+16]
            MOV [szFileName], EAX
            }

            比如我們用一個messagebox來彈出一個信息,說明該程序即將打開一個某謀路徑的文件句柄。但是有一個要注意的是,如果你想攔截遠程進程的話,對于那個攔截函數(shù)中所使用到的任何函數(shù)或者以任何形式的相對地址的調(diào)用都要停止。因為每個進程中的地址分配都是獨立的,比如上面的CALL MessageBoxA改成直接地址的調(diào)用。對于使用messagebox,我們應該定義一個函數(shù)指針,然后把這個指針的值賦值為user32.dll中導出該函數(shù)的直接地址。然后利用這個指針來進行函數(shù)調(diào)用。對于messagebox函數(shù)的調(diào)用可以這樣,在源程序中定義一個參數(shù)結構體,參數(shù)中包含一個導出函數(shù)的地址,把這個地址設為MessageBoxA的直接地址,獲取地址的方法就不說了。然后把這個參數(shù)傳給攔截函數(shù),就可以使用拉。這也是利用一個參數(shù)的原因。類似代碼如下:


            typedef struct _RemoteParam {
            ??DWORD dwMessageBox;
            } RemoteParam, * PRemoteParam;

            typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);//定義一個函數(shù)指針

            //攔截函數(shù)
            void HookCreateFile(LPVOID lParam)
            {
            RemoteParam* pRP = (RemoteParam*)lParam;//獲取參數(shù)地址
            char* PFileName = NULL;//定義一個指針
            __asm{
            MOV EAX,[EBP+16]
            MOV [szFileName], EAX //把CreateFile第一個參數(shù)的值,文件的路徑的地址傳?????????????????????? //給szFileName
            }

            //定 義一個函數(shù)指針
            PFN_MESSAGEBOX pfnMessageBox = (PFN_MESSAGEBOX)pRP->dwMessageBox;

            pfnMessageBox(NULL, PFileName, PFileName, MB_ICONINformATION |MB_OK);
            //輸出要打開的文件的路徑
            //.....
            }

            對于你要使用的其他函數(shù),都是使用同樣的方式,利用這個參數(shù)來傳遞我們要傳遞的函數(shù)的絕對地址,然后定義這個函數(shù)指針,就可以使用了。


            好了,接下來我們該讓被攔截的api正常工作了,這個不難,把他原來的數(shù)據(jù)恢復一下就可以了。那入口的10個字節(jié)。我們在改寫他們的時候應該保存一下,然后也把他放在參數(shù)中傳遞給攔截函數(shù),呵呵,參數(shù)的作用可多了。接著我們就可以用WriteProcessMemory函數(shù)來恢復這個api的入口了,代碼如下:
            PFN_GETCURRENTPROCESS pfnGetCurrentProcess = (PFN_GETCURRENTPROCESS)pRP->dwGetCurrentProcess;
            PFN_WRITEPROCESSMEMORY pfnWriteProcessMemory = (PFN_WRITEPROCESSMEMORY)pRP->dwWriteProcessMemory;
            ??
            if(!pfnWriteProcessMemory(pfnGetCurrentProcess(),
            ??????????????????????????????????????????(LPVOID)pfnConnect,
            ??????????????????????????????????????????(LPCVOID)pRP->szOldCode,
            ??????????????????????????????????????????10,
            ??????????????????????????????????????????NULL))
            pfnMessageBox(NULL, pRP->szModuleName1, pRP->szModuleName2,?? MB_ICONINformATION | MB_OK);
            其中這些函數(shù)指針的定義和上面的類似。
            而參數(shù)中的szoldcode則是在源程序中在修改api之前保存好,然后傳給攔截函數(shù),在源程序中是用ReadProcessMemory函數(shù)來獲取他的前10個字節(jié)的:
            ReadProcessMemory(GetCurrentProcess(),
            ????????????????????????????????(LPCVOID)RParam.dwCreateFile,
            ????????????????????????????????oldcode,
            ????????????????????????????????10,
            ????????????????????????????????&dwPid)
            strcat((char*)RParam.szOldCode, (char*)oldcode);


            接下來如果你還繼續(xù)保持對該api的攔截,則又該用WriteProcessMemory 來修改入口了,跟前面的恢復入口是一樣的,只不過把szOldCode換成了szNewCode了而已。這樣你又能對CreateFile繼續(xù)攔截了。

            好了,接下來該進行堆棧的清理了,也許你還要做點其他事情,盡管做去。但是清理堆棧是必須要做的,在函數(shù)結束的時候,因為在我們放任api恢復執(zhí)行之后,他又return 到我們的函數(shù)中來了,這個時候的堆棧是什么樣子呢?
            |EDI|?? <---ESP
            |ESI|
            |EBX|
            |局部變量|??
            |EBP|???? <---EBP
            |api入口之后的第11個字節(jié)的指令的地址|????
            |00010000|
            |createfile執(zhí)行后的下一條指令地址|
            |參數(shù)1|
            |參數(shù)2|
            |參數(shù)3|
            |參數(shù)4|
            |參數(shù)5|
            |參數(shù)6|
            |參數(shù)7|
            |..|

            我們的目標是把返回值記錄下來放到EAX寄存器中去,把返回地址記錄下來,同時把堆棧恢復成原來的樣子。
            首先我們恢復那些寄存器的值,接著釋放局部變量,可以用mov esp, ebp.因為我們不清楚具體的局部變量分配了多少空間。所以使用這個方法。


            __asm
            {POP EDI
            POP ESI
            POP EBX?? //恢復那些寄存器
            MOV EDX, [NextIpAddr]//把返回地址放到EDX中,因為待會兒?????????????????? //EBX被恢復后,線程中的所有局部變量就不能正常使用了。
            ????????
            MOV EAX, [Retvalue]//返回值放到EAX中,當然也可以修改這個返回值
            MOV ESP, EBP//清理局部變量
            POP EBP//恢復EBP的值
            ADD ESP, 28H?? //清理參數(shù)和返回地址,注意一共(7+1+1+1)*4
            PUSH EDX //把返回地址壓棧,這樣棧中就只有這一個返回地址了,返回之后棧?????????? //就空了
            RET
            }

            這樣,一切就完成了,堆棧恢復了應該有的狀態(tài),而你想攔截的也攔截到了。


            四:后記
            ??攔截的方式多種多樣,不過大體的思路卻都相同。要時刻注意你要攔截的函數(shù)的堆棧狀態(tài)以及在攔截函數(shù)中的對數(shù)據(jù)的引用和函數(shù)的調(diào)用(地址問題)。
            posted on 2006-11-18 16:51 獨孤九劍 閱讀(329) 評論(0)  編輯 收藏 引用 所屬分類: Win32Visual C++ 8.0
            精品久久久久一区二区三区| 97精品伊人久久大香线蕉| 精品久久久久香蕉网| 久久天天躁狠狠躁夜夜96流白浆| 久久久无码精品亚洲日韩蜜臀浪潮| 99久久国产热无码精品免费| 久久精品亚洲福利| 国产人久久人人人人爽| 亚洲国产一成久久精品国产成人综合 | 97精品国产91久久久久久| 久久精品国产精品亚洲精品 | 久久亚洲电影| 69久久夜色精品国产69| 理论片午午伦夜理片久久| 久久精品国产99久久无毒不卡 | 亚州日韩精品专区久久久| 国产精品久久久久影院色| 久久久久精品国产亚洲AV无码 | 久久精品国产亚洲Aⅴ香蕉| 日韩av无码久久精品免费| 色播久久人人爽人人爽人人片aV| 亚洲精品高清国产一线久久| 久久久精品日本一区二区三区| 亚洲第一极品精品无码久久| 三级韩国一区久久二区综合| 亚洲午夜久久影院| 久久99免费视频| 99国产欧美久久久精品蜜芽 | 久久精品国产精品青草app| 亚洲午夜久久久影院| 久久综合九色综合网站| 久久综合给合综合久久| 久久精品女人天堂AV麻| 国产成人精品久久一区二区三区av| 久久99精品久久久久久动态图| 伊人久久综合无码成人网| 99久久免费国产精品特黄| 精品久久久久久国产| 性做久久久久久久| 2021精品国产综合久久| 久久久久中文字幕|