<cite id="ffb66"></cite><cite id="ffb66"><track id="ffb66"></track></cite>
      <legend id="ffb66"><li id="ffb66"></li></legend>
      色婷婷久,激情色播,久久久无码专区,亚洲中文字幕av,国产成人A片,av无码免费,精品久久国产,99视频精品3
      網易首頁 > 網易號 > 正文 申請入駐

      游戲性能優化與逆向分析技術

      0
      分享至


      【USparkle專欄】如果你深懷絕技,愛“搞點研究”,樂于分享也博采眾長,我們期待你的加入,讓智慧的火花碰撞交織,讓知識的傳遞生生不息!

      這是侑虎科技第1878篇文章,感謝作者其樂陶陶供稿。歡迎轉發分享,未經作者授權請勿轉載。如果您有任何獨到的見解或者發現也歡迎聯系我們,一起探討。(QQ群:793972859)

      作者主頁:

      https://www.zhihu.com/people/jun-yan-76-80

      一、前言

      一直以來性能優化的工作,非常依賴于工具,從結果反推過程,采集產品運行時信息,反推生產環節中的問題,性能問題的定位其實就是在做各種逆向。

      不同的工具有不同的檢測面,一般會按照由粗及細的順序使用,直到找到問題的答案。

      • 粗粒度的工具,可大致定位到問題是出在哪個硬件上,比如發熱問題,可能的負載點在于CPU、GPU、其它硬件(屏幕、傳感器、網絡),一般應該是系統級的工具,常用的有Perfetto、Xcode、GamePerf、PerfDog。

      • 細粒度的工具,檢測面較窄,但能提供更深入的信息,比如:定位到是CPU的問題時,可使用Unity Profiler、Simpleperf看問題堆棧;當定位到是GPU的問題時,則使用RenderDoc、SnapdragonProfiler、Arm Graphics Analyzer截幀。

      打個比喻,粗粒度的工具好比地鐵,能帶你到大致的區域范圍,更細粒度的工具幫你解決最后一公里路,在實際情況中,“打通”一公里的問題往往是卡點,通用性質的工具可能滿足不了需求,常常做一些定制化的東西,通過一定積累,形成強大的工具鏈以應對各種突發問題,本文主要對于這些底層的技術棧做一些總結。

      二、動態庫注入

      Android系統的數據基本都能通過讀各種文件實現(統計線程,讀取CPU利用率/頻率),但有嚴格的權限限制,非root環境下,只能讀取自己進程相關的文件、內存信息。

      我們注入到目標進程的動態庫,就好像我們派出的“間諜”一樣,利用目標進程的身份執行我們自己的代碼。

      使用JDWP Shellifier是最常用的方式,我們用C++在NDK環境下編寫一個動態庫so文件,這個腳本利用Java調試服務加載我們自己的庫。這也是RenderDoc、?LoliProfiler、Matrix用的方式,需要應用Debug權限,或者root開全局調試,或者使用APKTool,解包修改AndroidManifest文件的Debug權限。


      https://github.com/IOActive/jdwp-shellifier

      這個腳本用Python封裝了注入過程,在onCreate函數觸發時,加載我們的庫。

      jdwp_start("127.0.0.1", 500, "android.app.Activity.onCreate", None, libname)


      控制臺輸出顯示注入成功

      當動態庫注入成功時,C++側入口函數JNI_OnLoad會被執行,我們就可以干自己想干的事情了,這只是打開大門的第一步。

      JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {     (void)reserved;     LOGI("JNI_OnLoad");     JNIEnv *env;     LOGI("------------------ 4000 : %d", (int)JNI_VERSION_1_6);     if (vm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK)     {         LOGI("JNI version not supported");         return JNI_ERR; // JNI version not supported.     }     else     {         LOGI("JNI init complete");     } }

      下一步介紹Hook技術,俗稱鉤子,能對特定函數劫持,兩種常見Hook手段為PLT Hook、Inline Hook。

      三、PLT Hook

      先大概講一下程序調用動態鏈接庫中函數的流程,以libunity.so中調用libc.so的Open函數為例子:會先訪問PLT(Procedure Linkage Table),第一次訪問它會使用動態連接器查找libc.so中Open函數的地址,然后地址保存到GOT(Global Offset Table)地址表,之后的調用就直接查GOT表了,如下:


      所謂的PLT Hook就是在這個過程做文章、鉆空子,比如xHook就是修改GOT表的函數地址為我們的自定義函數實現攔截,xHook是一個常用的庫,較多運用于各種工具底層實現,我們可以直接使用它,同時它也是開源的,我們可以參考它里面的很多代碼。


      https://github.com/iqiyi/xHookgithub.com/iqiyi/xHook

      PLT Hook比較適合去Hook一些公用庫的調用,不管上層怎么變,IO的行為最終落地到對Open、Close、Read、Wirte的調用,實際項目中主要用于IO、內存分配、線程、網絡等行為的監控,但它的局限性在于不能Hook內部函數,比如引擎內部的函數調用。

      四、實戰:打印引擎啟動時的IO調用

      隨便創建一個空的Demo,打包APK,將下面C++代碼通過NDK編譯成動態庫后,使用JDWP注入運行。

      這里在JNI_OnLoad函數創建一個新的線程,延遲3秒后再執行Hook的動作,是因為時機太早libunity.so未加載會導致失敗(據說xHook的作者后續開發了一個新的庫叫bHook,改進了這一點)。

           "xhook/xhook.h"         int MyOpen(const char *pathname, int flags, mode_t mode) {     int ret = open(pathname, flags, mode);     __android_log_print(ANDROID_LOG_INFO, "TestHook", "unity open %s %d", pathname, ret);     return ret; } void TestHook() {     // 延遲3秒,等待Unity加載完成     std::this_thread::sleep_for(std::chrono::seconds(3));     // 對Open函數Hook注冊     xhook_register("libunity.so", "open", (void *)MyOpen, nullptr);     // 執行Hook     xhook_refresh(0); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {     JNIEnv *env;     if (vm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK)     {         return JNI_ERR; // JNI version not supported.     }     std::thread(TestHook).detach();     return JNI_VERSION_1_6; }

      這樣我們可以觀察到Unity啟動時加載的一些東西:

      正在加載obb文件


      正在加載il2cpp.so

      五、Inline Hook

      前面提到,PLT Hook不能Hook到庫內部的函數調用,這個時候就應該輪到Inline Hook出場,它是通過對目標函數地址插入跳轉指令實現,理論上可以Hook住任意內部函數,功能更為強大,由于涉及到在不同CPU架構上的運行狀態機器碼修改,看起來很復雜,其實一點也不簡單,雖兼容性不如PLT Hook,不推薦在生產環境使用,但作為測試環境中的性能工具還是很強的。

      ShadowHook是我常用的庫,可以將它的C++源碼下載下來,和自己庫一起編譯。


      https://github.com/bytedance/android-inline-hook

      如果Hook的目標庫是帶符號表的,可以通過函數名hook,像這樣:

      stub = shadowhook_hook_sym_name(                "libart.so",                "_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc",                (void *)proxy,                (void **)&orig);

      但是我們常見的libunity.so、libil2cpp.so的符號表是分離的,可以嘗試用llvm-objcopy合并回去,這里更推薦另一種做法,ShadowHook也可以直接通過函數地址進行Hook

      void *shadowhook_hook_func_addr(     void *func_addr,     void *new_addr,     void **orig_addr);

      這里的func_addr函數地址是絕對地址,為動態庫基地址、函數偏移地址之和,找到這兩個地址加起來就行。

      動態庫基地址每次進程啟動都不一樣,需要我們在程序中動態獲取,可以通過dl_iterate_phdr(Android 5.0以上)獲取,也可以讀/proc/self/maps實現(Android 4.0版本以上),之前介紹的xHook有源碼可以抄一下。


      /proc/self/maps能查詢到動態庫基地址

      而函數的偏移地址可以使用NDK下llvm-readelf -s指令,讀取符號表獲取到:


      readelf讀取出的引擎內部函數地址

      接下來,對函數Hook后,需要對參數進行內存分析提取里面的有用信息,如果有源碼,就是開卷考試,按照其內存布局定義出來;沒源碼,我們也可以通過一些技巧把信息提取出來,下面以實戰說明一下。

      六、實戰:統計引擎內部調用

      我曾經在《使用Simpleperf+Timeline診斷游戲卡頓》[1]這一篇文章中提到過,一些常見的卡頓歸因,能通過Simpleperf識別,但我們只知道觸發堆棧,今天我們更進一步。

      這里以AddComponent函數為例,做一個Demo,然后嘗試使用Hook把觸發的GameObject、組件名字都打印出來,C# 測試代碼如下:

      // New Game Object節點添加一些Unity內置組件 var go = newGameObject(); go.AddComponent (); go.AddComponent (); go.AddComponent (); // 相機節點添加一個自定義腳本組件 gameObjet.AddComponent ();

      通過Simpleperf鎖定我們的目標函數為AddComponent(GameObject&, Unity::Type const*, ScriptingClassPtr, core::basic_string >*)


      Simpleperf-Timeline查看命中的native函數

      接下來通過llvm-readelf -s指令,查詢函數在符號表中的位置,名字稍微和Simpleperf中的顯示形式有點區別,但是我們還是能認出它,它的地址就是0x5126a4。


      搜索符號表內AddComponent函數地址

      接下來,我們需要在代理函數里面,對函數參數做一些解析,從函數簽名可以看到,參數有4個:void *go、void *unitytype、void *scriptclassptr和void *error。

      我們的目標是獲取節點名和組件名,解析前3個就行,主要有兩種方案:

      1. 在符號表里多收集一些工具函數地址,比如獲取GameObject名字的方法0x435010,這個方法傳入GameObject對象指針作為參數,返回名字字符串,所以可以把這個函數地址存起來,直接調用,我管這叫“他山之石,可以攻玉”。


      獲取GameObject名字的方法地址能輕易搜索到

      2. 針對另外兩個參數,可以將結構直接定義出來使用,比如ScriptClass前兩個參數是指針,第三個就是C字符串。這些工作,在有相關源碼的情況下會容易很多,如果沒有的話,只能通過LLDB無源碼動態調試之類的手段來獲取其內存布局,會涉及到一些二進制分析手段、工具。

      有了這些準備工作,就可以開始編碼了:

           "shadowhook.h"          classScriptclass {     public:         void *placeholder1;         void *placeholder2;         constchar *name; }; classUnityType {     public:         void *placeholder1;         void *placeholder2;         constchar *name; }; uintptr_t baseaddr = 0; int callback(struct dl_phdr_info *info, size_t size, void *data) {     constchar *target = (constchar *)data;     // Check if the current shared library is the target library     if (strstr(info->dlpi_name, target))     {         __android_log_print(ANDROID_LOG_INFO, "TestHook", "Base address of %s: 0x%lx\n", target, (unsigned long)info->dlpi_addr);         baseaddr = info->dlpi_addr;         return1; // Return 1 to stop further iteration     }     return0; // Continue iteration } void *old_AddComponent = nullptr; typedef void *(*AddComponentFunc)(void *go, void *unitytype, void *scriptclassptr, void *error); typedef constchar*(*GameObjectGetNameFunc)(void *ptr); void *MyAddComponent(void *go, void *unitytype, void *scriptclassptr, void *error) {     constchar *goName = nullptr;     constchar *typeName = nullptr;     if(go != nullptr)     {         // 計算GameObjectGetName的地址         uintptr_t addr = baseaddr + 0x435010;          // 調用GameObjectGetName獲取名稱         GameObjectGetNameFunc func = (GameObjectGetNameFunc)(addr);         goName = func(go);     }     if (scriptclassptr != nullptr)     {         Scriptclass *t = (Scriptclass *)scriptclassptr;         typeName = t->name;     }     elseif (unitytype != nullptr)     {         UnityType *t = (UnityType *)unitytype;         typeName = t->name;     }     if(goName == nullptr)         goName = "null";     if(typeName == nullptr)         typeName = "null";     __android_log_print(ANDROID_LOG_INFO, "TestHook", "UnityAddComponent: %s %s\n", goName, typeName);     return ((AddComponentFunc)old_AddComponent)(go, unitytype, scriptclassptr, error); } void TestHook() {     // 延遲3秒,等待Unity加載完成     std::this_thread::sleep_for(std::chrono::seconds(3));     // 查詢libunity的基地址     constchar *library_name = "libunity.so";     dl_iterate_phdr(callback, (void *)library_name);     // 計算AddComponent的函數地址     uintptr_t addr = baseaddr + 0x5126a4;     // 執行Hook并保存原函數地址到old_AddComponent     void *stub = shadowhook_hook_func_addr((void *)addr, (void *)MyAddComponent, (void **)&old_AddComponent);     if (stub == nullptr)     {         int err_num = shadowhook_get_errno();         constchar *err_msg = shadowhook_to_errmsg(err_num);         __android_log_print(ANDROID_LOG_INFO, "TestHook", "hook error %d - %s\n", err_num, err_msg);     }     else     {         __android_log_print(ANDROID_LOG_INFO, "TestHook", "hook success\n");     } } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {     JNIEnv *env;     if (vm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK)     {         return JNI_ERR; // JNI version not supported.     }     // 初始化Shadowhook     int ret = shadowhook_init(SHADOWHOOK_MODE_UNIQUE, true);     if (ret != 0)     {         constchar *err_msg = shadowhook_to_errmsg(shadowhook_get_init_errno());         __android_log_print(ANDROID_LOG_INFO, "TestHook", "init error %d - %s\n", shadowhook_get_init_errno(), err_msg);     }     else     {         __android_log_print(ANDROID_LOG_INFO, "TestHook", "init success\n");     }     std::thread(TestHook).detach();     return JNI_VERSION_1_6; }

      和前面PLT Hook的例子一樣,使用JDWP注入執行,最終可以輸出Demo中調用AddComponet的參數詳情,利用這些信息,接下來就可以做很多事情了,我們現在可以幾乎Hook任意函數!


      控制臺最終能正常輸出節點、組件名

      七、棧回溯

      在棧上每個函數都有自己的儲存空間,被稱之為棧幀(Frame),上面保存了部分參數、局部變量。當調用其它函數時,會將這個函數返回后的下一行指令地址也保存在棧幀,棧回溯就是分析這些棧上面函數地址,還原函數運行軌跡的過程。


      函數A調用函數B,0x40056a是函數B結束后返回的地址

      棧回溯經常和Hook一起配合,當Hook住某個函數后,輸出它的調用棧,能更進一步分析問題歸因,如果對性能要求不高,可以直接使用libunwind庫,它在不需要開-fno-omit-frame-pointer編譯選項、dwarf調試信息的情況下,也能輸出函數地址,然后我們通過符號表將函數名解析出來。

          // 棧回溯上下文結構 struct BacktraceState {     void **current;     void **end; }; static _Unwind_Reason_Code UnwindCallback(struct _Unwind_Context *context, void *arg) {     BacktraceState *state = static_cast (arg);     uintptr_t pc = _Unwind_GetIP(context);     if (pc)     {         if (state->current == state->end)         {             return _URC_END_OF_STACK;         }         else         {             *state->current++ = reinterpret_cast
      
       (pc);         }     }     return _URC_NO_REASON; } size_t CaptureBacktrace(void **buffer, size_t max) {     BacktraceState state = {buffer, buffer + max};     _Unwind_Backtrace(UnwindCallback, &state);     return state.current - buffer; } void DumpBacktrace(std::ostream &os, void **buffer, size_t count) {     for (size_t idx = 0; idx < count; ++idx)     {         constvoid *addr = buffer[idx];         constchar *symbol = "";         Dl_info info;         if (dladdr(addr, &info) && info.dli_sname)         {             symbol = info.dli_sname;         }         // 這里將函數的絕對地址轉換為相對地址         uintptr_t relative = (uintptr_t)addr - (uintptr_t)info.dli_fbase;         os << "  #" << std::setw(2) << idx << ": " << info.dli_fname << " " << (void *)relative << "\n";     } } // 經封裝后的打印函數 void PrintStacktrace(const size_t count) {     void* buffer[count];     std::ostringstream oss;     DumpBacktrace(oss, buffer, CaptureBacktrace(buffer, count));     __android_log_print(ANDROID_LOG_INFO, "TestHook", oss.str().c_str()); }
      

      棧回溯的步驟雖然看起來繁瑣,但只要經過封裝后,使用起來其實和在C# 里面一樣方便,下一步我們來試一下。

      八、實戰:為IO調用加入棧統計

      沿用之前的PLT Hook的例子,這次我們將調用堆棧打印出來:


      調用封裝好的PrintStacktrace


      現在打印日志里多了調用棧函數地址

      使用NDK目錄下的addr2line.exe對這些地址進行解析,最終得到我們想要的結果。

      LocalFileSystemPosix::Open(FileEntryData&, FilePermission, FileAutoBehavior) zip::CentralDirectory::Enumerate(bool (*)(FileSystemEntry const&, FileAccessor&, char const*, zip::CDFD const&, void*), void*) VerifyAndMountObb(char const*) MountObbs() UnityPause(int) UnityPlayerLoop() nativeRender(_JNIEnv*, _jobject*)

      九、結語

      本文從以性能優化分析目的入手,介紹了常用的逆向分析手段 —— 注入、Hook、堆棧回溯,這里只是淺顯地聊了一下運用場景,事實上每一個坑都能挖到很深,比如注入與反注入,如何對競品進行注入,Hook的相關調試方法、內存分析、更高性能的棧回溯、聚合顯示(火焰圖)等等。

      之所以總結此文,是因為我在近期的工作中感覺到,了解一點逆向分析的知識,對性能優化、程序調試方面很有好處,也不局限于游戲開發領域,技多不壓身。

      參考

      [1] 使用Simpleperf+Timeline診斷游戲卡頓


      https://zhuanlan.zhihu.com/p/666443120

      文末,再次感謝其樂陶陶 的分享, 作者主頁:https://www.zhihu.com/people/jun-yan-76-80, 如果您有任何獨到的見解或者發現也歡迎聯系我們,一起探討。(QQ群: 793972859 )。

      近期精彩回顧

      【學堂上新】

      【學堂上新】

      【學堂上新】

      【萬象更新】

      特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。

      Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

      相關推薦
      熱點推薦
      這事是真的嗎?

      這事是真的嗎?

      西樓飲月
      2025-12-24 23:30:03
      小孩子哪里會撒謊呢?美國一萌娃走丟,畫出其父畫像,超級抽象

      小孩子哪里會撒謊呢?美國一萌娃走丟,畫出其父畫像,超級抽象

      趣筆談
      2025-12-25 10:42:34
      只施壓泰國停火,卻縱容柬方埋雷?泰防長怒批:該國中立是幌子!

      只施壓泰國停火,卻縱容柬方埋雷?泰防長怒批:該國中立是幌子!

      朔方瞭望
      2025-12-24 16:35:47
      江西49歲女園長駕車墜泥塘致8死,小兒也在車上,丈夫是校車司機

      江西49歲女園長駕車墜泥塘致8死,小兒也在車上,丈夫是校車司機

      Mr王的飯后茶
      2025-12-24 15:40:24
      切爾西的痛:26歲菲利克斯19場造22球,進球數壓C羅+領跑射手榜

      切爾西的痛:26歲菲利克斯19場造22球,進球數壓C羅+領跑射手榜

      側身凌空斬
      2025-12-25 09:16:07
      難以置信的三起事件,真就發生了!

      難以置信的三起事件,真就發生了!

      新海言
      2025-12-24 19:26:10
      黎筍之子黎堅誠坦言:父親選擇同中國開戰,是其畢生最大的失策

      黎筍之子黎堅誠坦言:父親選擇同中國開戰,是其畢生最大的失策

      磊子講史
      2025-12-24 11:04:05
      美媒評本世紀最佳陣:詹科庫杜鄧肯入選一陣,奧尼爾僅三陣

      美媒評本世紀最佳陣:詹科庫杜鄧肯入選一陣,奧尼爾僅三陣

      懂球帝
      2025-12-25 07:52:31
      商丘一空地現成堆被丟棄的全新手機殼,多人驅車前往“撿漏”,有人一次撿了50個

      商丘一空地現成堆被丟棄的全新手機殼,多人驅車前往“撿漏”,有人一次撿了50個

      極目新聞
      2025-12-24 21:09:37
      中美休戰結束?美售臺百億軍備,中方大拋美債,送川普一句話

      中美休戰結束?美售臺百億軍備,中方大拋美債,送川普一句話

      boss外傳
      2025-12-24 12:00:03
      為什么龐家敢硬剛南京博物館,龐家的底蘊你想不到

      為什么龐家敢硬剛南京博物館,龐家的底蘊你想不到

      鶴羽說個事
      2025-12-25 11:39:22
      曝徐湖平已被帶走,前一天晚開了一夜燈,更多謊言被戳穿

      曝徐湖平已被帶走,前一天晚開了一夜燈,更多謊言被戳穿

      古希臘掌管松餅的神
      2025-12-24 13:29:23
      呂良偉70大壽:楊受成彎腰舉杯、章小惠胖出水桶腰、王晶鍋蓋頭

      呂良偉70大壽:楊受成彎腰舉杯、章小惠胖出水桶腰、王晶鍋蓋頭

      瘋說時尚
      2025-12-25 09:48:02
      鬧大了!中J某局的員工在網上掀桌子了!

      鬧大了!中J某局的員工在網上掀桌子了!

      黯泉
      2025-12-24 20:53:06
      駐柬大使汪文斌人民日報撰文

      駐柬大使汪文斌人民日報撰文

      政知新媒體
      2025-12-25 09:26:56
      三個信號表明,美準備打大仗,六國已被鎖定,卻避開了中國這條線

      三個信號表明,美準備打大仗,六國已被鎖定,卻避開了中國這條線

      梁訊
      2025-12-25 10:36:48
      冬至后,這4種激素水果一定要少買!果販子透露:不要錢都不吃

      冬至后,這4種激素水果一定要少買!果販子透露:不要錢都不吃

      阿龍美食記
      2025-12-24 10:52:39
      上海46歲獨居女子過世,遺產不能買墓地?最新消息:法院指定虹口區民政局任遺產管理人

      上海46歲獨居女子過世,遺產不能買墓地?最新消息:法院指定虹口區民政局任遺產管理人

      每日經濟新聞
      2025-12-25 00:48:23
      KK園區491棟違法建筑已拆除

      KK園區491棟違法建筑已拆除

      財聯社
      2025-12-24 17:38:43
      馬斯克預測美國經濟18個月內實現兩位數增長,AI成關鍵驅動力

      馬斯克預測美國經濟18個月內實現兩位數增長,AI成關鍵驅動力

      華爾街見聞官方
      2025-12-25 05:42:06
      2025-12-25 13:35:00
      侑虎科技UWA incentive-icons
      侑虎科技UWA
      游戲/VR性能優化平臺
      1532文章數 986關注度
      往期回顧 全部

      科技要聞

      屠龍少年被"招安"!英偉達平安夜豪擲200億

      頭條要聞

      女子入室殺害好友三名未成年子女 隨后在樓內上吊自殺

      頭條要聞

      女子入室殺害好友三名未成年子女 隨后在樓內上吊自殺

      體育要聞

      單賽季11冠,羽壇“安洗瑩時代”真的來了

      娛樂要聞

      金莎小19歲男友求婚成功!兩人雪地擁吻

      財經要聞

      美國未來18個月不對中國芯片加額外關稅

      汽車要聞

      預售31.3萬元起 全新奧迪Q5L將于1月內上市

      態度原創

      健康
      本地
      房產
      親子
      公開課

      這些新療法,讓化療不再那么痛苦

      本地新聞

      云游安徽|亳州晨暮皆成史,街巷縱橫印春秋

      房產要聞

      硬核!央企海口一線江景頂流紅盤,上演超預期交付!

      親子要聞

      寶寶紅鼻子真相!90%媽媽都忽略的細節

      公開課

      李玫瑾:為什么性格比能力更重要?

      無障礙瀏覽 進入關懷版 主站蜘蛛池模板: 日韩激情无码免费毛片| 亚洲天堂无码| 日本高清一区| 99r精品在线| 抚宁县| 精品一区二区三区波多野结衣| 亚洲成在人网站无码天堂| 内射囯产旡码丰满少妇| 日韩中文字幕区一区有砖一区| 国产在线无码视频一区二区三区| 亚洲一区二区在线无码| 国产精品福利自产拍在线观看| 久久精品6| 阳谷县| 久久久久久亚洲精品a片成人| 大伊香蕉在线精品视频75| 亚洲人成人77777网站| 无码伊人久久大杳蕉中文无码| 亚州性色| 清流县| 国产精品无码久久久久| 久久国产精品-国产精品| 夜夜高潮夜夜爽国产伦精品| 男女性高爱潮免费网站| 国产VA视频| 大田县| 久久精品国产99精品国产2021| 成年免费视频黄网站zxgk| 日韩 欧美 亚洲 一区二区| 亚洲综合伊人久久大杳蕉| 日日噜噜夜夜久久亚洲一区二区| 亚洲AV无码乱码在线观看性色| 亚洲AV人人澡人人人夜| 国产乱子伦一区二区三区四区五区| 免费观看成人欧美www色| 久久青青草原亚洲AV无码麻豆| 中文日韩欧美| 欧洲AV在线| 日韩人妻精品无码制服| 国产精久久一区二区三区| 无码精品尤物一区二区三区|