<ruby id="9ue20"></ruby>

  1. 
    

      国产午夜福利免费入口,国产日韩综合av在线,精品久久人人妻人人做精品,蜜臀av一区二区三区精品,亚洲欧美中文日韩在线v日本,人妻av中文字幕无码专区 ,亚洲精品国产av一区二区,久久精品国产清自在天天线
      網易首頁 > 網易號 > 正文 申請入駐

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

      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.

      相關推薦
      熱點推薦
      菲律賓一夜變天!伊梅臨陣反水,13票把親弟盟友踢出參議院

      菲律賓一夜變天!伊梅臨陣反水,13票把親弟盟友踢出參議院

      你的雷達站
      2026-05-13 14:36:47
      考辛斯談詹姆斯:你們都看過勒布朗打球,我覺得他還能再打十年

      考辛斯談詹姆斯:你們都看過勒布朗打球,我覺得他還能再打十年

      好火子
      2026-05-14 03:24:07
      中方已做最壞準備,一旦中美爆發戰爭,中國三大底牌一個比一個狠

      中方已做最壞準備,一旦中美爆發戰爭,中國三大底牌一個比一個狠

      阿校談史
      2026-05-14 00:12:02
      做藝人沒有藝德!在上海被抓捕的 4 位明星,你們知道都有誰嗎?

      做藝人沒有藝德!在上海被抓捕的 4 位明星,你們知道都有誰嗎?

      她時尚丫
      2026-02-17 21:56:13
      CBA鐵律無人能破,常規賽前4搶第一,上海被看好,衛冕冠軍有些難

      CBA鐵律無人能破,常規賽前4搶第一,上海被看好,衛冕冠軍有些難

      萌蘭聊個球
      2026-05-14 07:17:34
      金智秀哥哥否認性侵女主播,稱女方主動進臥室,D社公開雙方采訪

      金智秀哥哥否認性侵女主播,稱女方主動進臥室,D社公開雙方采訪

      韓小娛
      2026-05-12 17:51:52
      伊萬卡流淚了

      伊萬卡流淚了

      新浪財經
      2026-05-05 19:51:47
      別只盯特朗普專機,魯比奧還是來了,釋放比訪問更重要的信號

      別只盯特朗普專機,魯比奧還是來了,釋放比訪問更重要的信號

      蘭妮搞笑分享
      2026-05-13 09:06:32
      瑪格特·羅比機場穿搭曝光,這條43美元牛仔褲火了

      瑪格特·羅比機場穿搭曝光,這條43美元牛仔褲火了

      追星雷達站
      2026-05-13 06:04:31
      美國對中國統一下達新結論:大陸只要按兵不動,越晚統一代價越小

      美國對中國統一下達新結論:大陸只要按兵不動,越晚統一代價越小

      華史談
      2026-05-13 08:49:26
      38歲向佑的終極報復,公開牽手南昌48歲金牙姐,向太這次真急了

      38歲向佑的終極報復,公開牽手南昌48歲金牙姐,向太這次真急了

      一盅情懷
      2026-05-11 16:54:37
      比電子布還猛?光模塊核心器件—晶振需求爆發  A股僅5家公司布局

      比電子布還猛?光模塊核心器件—晶振需求爆發 A股僅5家公司布局

      元芳說投資
      2026-05-14 06:30:14
      大跌64%!阿里“失速”

      大跌64%!阿里“失速”

      杠桿游戲
      2026-05-13 22:35:24
      菲律賓參議院內響起槍聲!有軍方人員持槍入內

      菲律賓參議院內響起槍聲!有軍方人員持槍入內

      看看新聞Knews
      2026-05-13 22:52:04
      黃圣依離婚真選對了,楊子媽媽太會演戲,對楊子新女友滿臉寵溺

      黃圣依離婚真選對了,楊子媽媽太會演戲,對楊子新女友滿臉寵溺

      觀魚聽雨
      2026-05-13 23:19:31
      曾發涉港不當言論!NBA名記Shams:76人隊解雇總經理莫雷

      曾發涉港不當言論!NBA名記Shams:76人隊解雇總經理莫雷

      全景體育V
      2026-05-13 08:14:35
      沉默45年,中國第二輪"嚴打"終于來了!目標改變總體戰正式打響

      沉默45年,中國第二輪"嚴打"終于來了!目標改變總體戰正式打響

      薦史
      2026-05-03 13:48:04
      1.6億美金!湖人為何必須砸鍋賣鐵留住里夫斯?

      1.6億美金!湖人為何必須砸鍋賣鐵留住里夫斯?

      仰臥撐FTUer
      2026-05-13 17:47:01
      江蘇多地出現彩色云朵 專家:一種大氣光學現象

      江蘇多地出現彩色云朵 專家:一種大氣光學現象

      環球網資訊
      2026-05-12 21:53:56
      新加坡急了,外長幾乎是拍著桌子,讓中國“尊重”馬六甲的地位。

      新加坡急了,外長幾乎是拍著桌子,讓中國“尊重”馬六甲的地位。

      南權先生
      2026-01-26 15:41:26
      2026-05-14 08:03:00
      侑虎科技UWA incentive-icons
      侑虎科技UWA
      游戲/VR性能優化平臺
      1575文章數 987關注度
      往期回顧 全部

      科技要聞

      阿里年營收首破萬億,AI終于不再是畫大餅

      頭條要聞

      中東戰火燒痛印度 莫迪六天訪五國要外交“救國”

      頭條要聞

      中東戰火燒痛印度 莫迪六天訪五國要外交“救國”

      體育要聞

      14年半,74萬,何冰嬌沒選那條更安穩的路

      娛樂要聞

      白鹿掉20萬粉,網友為李晨鳴不平

      財經要聞

      美國總統特朗普抵達北京

      汽車要聞

      C級純電轎跑 吉利銀河"TT"申報圖來了

      態度原創

      藝術
      家居
      本地
      游戲
      公開課

      藝術要聞

      這才是真正的“史上最強畢業證”,書法堪比字帖!

      家居要聞

      內在自敘,無域有方

      本地新聞

      用蘇繡的方式,打開江西婺源

      三角洲行動那些玄學設定,很多玩家可能都經歷過這些事情

      公開課

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

      無障礙瀏覽 進入關懷版 主站蜘蛛池模板: 国产不卡一区在线视频| 亚洲一级AV| 精品无码av无码专区| 漂亮的保姆hd完整版免费韩国 | 亚洲国产欧美在线人成人| 蜜芽久久人人超碰爱香蕉| 亚洲成人视屏| 久久久久久久久18禁秘| 狠狠色综合TV久久久久久| 國產尤物AV尤物在線觀看| 日韩AV无码精品| 伊人欧美在线| 人妻少妇精品无码专区二区| 中文字幕av不卡电影网| 最新精品国偷自产在线 | 亚洲人成网网址在线看| 国产av一区二区三区| 亚洲精品一区丝袜无码| 波多野结衣久久一区二区| 岛国精品在线播放一区| 亚洲精品成人在线2| 国内精品日本久久久久影院| 伊人成伊人成综合网222| av亚洲一区| 国产女同一区二区在线| 欧美性网| 亚洲色A| 久久夜色精品国产亚av| 久久精品国产亚洲黑森林| 国产伦精品一区二区三区视频痴汉 | 亚洲成人动漫在线观看| 性色AV一区二区三区咪爱四虎| 国产一区二区不卡在线视频 | 一本伊人久久| 丰满少妇αⅴ无码区| 亚洲国产成人综合熟女| 婷婷五月在线视频| 亚洲综合区小说区激情区噜噜 | 欧美a在线| 一本色道久久综合亚洲精品按摩| 欧美另类图区清纯亚洲|