国产av一二三区|日本不卡动作网站|黄色天天久久影片|99草成人免费在线视频|AV三级片成人电影在线|成年人aV不卡免费播放|日韩无码成人一级片视频|人人看人人玩开心色AV|人妻系列在线观看|亚洲av无码一区二区三区在线播放

網(wǎng)易首頁 > 網(wǎng)易號 > 正文 申請入駐

游戲性能優(yōu)化與逆向分析技術(shù)

0
分享至


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

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

作者主頁:

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

一、前言

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

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

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

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

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

二、動態(tài)庫注入

Android系統(tǒng)的數(shù)據(jù)基本都能通過讀各種文件實現(xiàn)(統(tǒng)計線程,讀取CPU利用率/頻率),但有嚴格的權(quán)限限制,非root環(huán)境下,只能讀取自己進程相關(guān)的文件、內(nèi)存信息。

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

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


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

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

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


控制臺輸出顯示注入成功

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

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技術(shù),俗稱鉤子,能對特定函數(shù)劫持,兩種常見Hook手段為PLT Hook、Inline Hook。

三、PLT Hook

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


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


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

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

四、實戰(zhàn):打印引擎啟動時的IO調(diào)用

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

這里在JNI_OnLoad函數(shù)創(chuàng)建一個新的線程,延遲3秒后再執(zhí)行Hook的動作,是因為時機太早libunity.so未加載會導(dǎo)致失?。〒?jù)說xHook的作者后續(xù)開發(fā)了一個新的庫叫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函數(shù)Hook注冊     xhook_register("libunity.so", "open", (void *)MyOpen, nullptr);     // 執(zhí)行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到庫內(nèi)部的函數(shù)調(diào)用,這個時候就應(yīng)該輪到Inline Hook出場,它是通過對目標函數(shù)地址插入跳轉(zhuǎn)指令實現(xiàn),理論上可以Hook住任意內(nèi)部函數(shù),功能更為強大,由于涉及到在不同CPU架構(gòu)上的運行狀態(tài)機器碼修改,看起來很復(fù)雜,其實一點也不簡單,雖兼容性不如PLT Hook,不推薦在生產(chǎn)環(huán)境使用,但作為測試環(huán)境中的性能工具還是很強的。

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


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

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

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

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

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

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

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


/proc/self/maps能查詢到動態(tài)庫基地址

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


readelf讀取出的引擎內(nèi)部函數(shù)地址

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

六、實戰(zhàn):統(tǒng)計引擎內(nèi)部調(diào)用

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

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

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

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


Simpleperf-Timeline查看命中的native函數(shù)

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


搜索符號表內(nèi)AddComponent函數(shù)地址

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

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

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


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

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

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

     "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;          // 調(diào)用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的函數(shù)地址     uintptr_t addr = baseaddr + 0x5126a4;     // 執(zhí)行Hook并保存原函數(shù)地址到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注入執(zhí)行,最終可以輸出Demo中調(diào)用AddComponet的參數(shù)詳情,利用這些信息,接下來就可以做很多事情了,我們現(xiàn)在可以幾乎Hook任意函數(shù)!


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

七、棧回溯

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


函數(shù)A調(diào)用函數(shù)B,0x40056a是函數(shù)B結(jié)束后返回的地址

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

    // ?;厮萆舷挛慕Y(jié)構(gòu) 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;         }         // 這里將函數(shù)的絕對地址轉(zhuǎn)換為相對地址         uintptr_t relative = (uintptr_t)addr - (uintptr_t)info.dli_fbase;         os << "  #" << std::setw(2) << idx << ": " << info.dli_fname << " " << (void *)relative << "\n";     } } // 經(jīng)封裝后的打印函數(shù) 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()); }

?;厮莸牟襟E雖然看起來繁瑣,但只要經(jīng)過封裝后,使用起來其實和在C# 里面一樣方便,下一步我們來試一下。

八、實戰(zhàn):為IO調(diào)用加入棧統(tǒng)計

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


調(diào)用封裝好的PrintStacktrace


現(xiàn)在打印日志里多了調(diào)用棧函數(shù)地址

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

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*)

九、結(jié)語

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

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

參考

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


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

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

近期精彩回顧

【學堂上新】

【學堂上新】

【學堂上新】

【萬象更新】

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

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.

相關(guān)推薦
熱點推薦
伊朗掀桌子了

伊朗掀桌子了

梳子姐
2026-03-02 12:25:02
如果我退休也能拿到這么高的退休金就好了…

如果我退休也能拿到這么高的退休金就好了…

慧翔百科
2026-03-02 12:02:37
被中國制裁后,日本一片哀嚎,高市決定:5年內(nèi)在中國東邊反擊

被中國制裁后,日本一片哀嚎,高市決定:5年內(nèi)在中國東邊反擊

愛下廚的阿釃
2026-03-02 17:00:30
C羅獲邀重返歐洲足壇!主帥直言:他若來此踢球?qū)⑹菈艋脮r刻

C羅獲邀重返歐洲足壇!主帥直言:他若來此踢球?qū)⑹菈艋脮r刻

臻體育
2026-03-01 22:41:03
全劇終!大S遺產(chǎn)分完,S媽分文沒有,兩女婿賺翻,小S要賺錢養(yǎng)媽

全劇終!大S遺產(chǎn)分完,S媽分文沒有,兩女婿賺翻,小S要賺錢養(yǎng)媽

動物奇奇怪怪
2026-03-02 16:30:47
都被史記騙了!春秋戰(zhàn)國打仗有50萬大軍,明清湊15萬兵力都費勁?

都被史記騙了!春秋戰(zhàn)國打仗有50萬大軍,明清湊15萬兵力都費勁?

老達子
2026-03-02 06:40:03
產(chǎn)蛋比不過雞,長肉比不過鴨,人類為何要馴服兇猛的大鵝?

產(chǎn)蛋比不過雞,長肉比不過鴨,人類為何要馴服兇猛的大鵝?

翰林冷知識
2026-03-01 11:52:05
謝賢前女友CoCo,終為自己的口無遮攔買了單,張柏芝早有前車之鑒

謝賢前女友CoCo,終為自己的口無遮攔買了單,張柏芝早有前車之鑒

以茶帶書
2026-02-28 13:04:19
3月30日起執(zhí)行!國務(wù)院824號令:農(nóng)村土葬老墳處置規(guī)則與權(quán)益保護

3月30日起執(zhí)行!國務(wù)院824號令:農(nóng)村土葬老墳處置規(guī)則與權(quán)益保護

三農(nóng)雷哥
2026-02-26 19:39:24
今年,北京已無離職潮

今年,北京已無離職潮

微微熱評
2026-03-01 18:45:40
伊朗形勢越來越危急,歐洲三大強國發(fā)表聲明,已經(jīng)決定下場參戰(zhàn)

伊朗形勢越來越危急,歐洲三大強國發(fā)表聲明,已經(jīng)決定下場參戰(zhàn)

小小科普員
2026-03-02 16:36:20
歷史第一!亞歷山大30分超張伯倫 雷霆三殺獨行俠鞏固西部第一

歷史第一!亞歷山大30分超張伯倫 雷霆三殺獨行俠鞏固西部第一

醉臥浮生
2026-03-02 11:14:56
“太恐怖,iPhone半夜自己給陌生人打47分鐘電話!”

“太恐怖,iPhone半夜自己給陌生人打47分鐘電話!”

都市快報橙柿互動
2026-02-25 11:28:41
梅婷有過三段婚姻,第一任丈夫是葉挺的孫子葉大鷹,是電影導(dǎo)演

梅婷有過三段婚姻,第一任丈夫是葉挺的孫子葉大鷹,是電影導(dǎo)演

百態(tài)人間
2026-03-02 15:16:23
男子高鐵商務(wù)座車廂內(nèi)抽煙,還脫鞋將雙腳架在車窗處,12306客服回應(yīng):全列禁煙,遇到可舉報

男子高鐵商務(wù)座車廂內(nèi)抽煙,還脫鞋將雙腳架在車窗處,12306客服回應(yīng):全列禁煙,遇到可舉報

都市快報橙柿互動
2026-03-02 12:56:36
男籃世預(yù)賽后大名單或變動!郭士強該下狠手了:張鎮(zhèn)麟恐被淘汰?

男籃世預(yù)賽后大名單或變動!郭士強該下狠手了:張鎮(zhèn)麟恐被淘汰?

籃球快餐車
2026-03-02 17:10:21
第九波打擊開始!伊朗襲擊美航母,特朗普惱羞成怒,英法德或參戰(zhàn)

第九波打擊開始!伊朗襲擊美航母,特朗普惱羞成怒,英法德或參戰(zhàn)

林子說事
2026-03-02 16:46:08
汪小菲責怪母親發(fā)他和孩子的視頻,網(wǎng)友評價:不要繼承麻六記

汪小菲責怪母親發(fā)他和孩子的視頻,網(wǎng)友評價:不要繼承麻六記

萱小蕾o
2026-03-02 13:31:09
法國表示“準備參與”海灣國家的防御行動

法國表示“準備參與”海灣國家的防御行動

環(huán)球網(wǎng)資訊
2026-03-02 17:34:06
汪小菲喜得三胎僅2天,荒唐事接連發(fā)生,馬筱梅或走上大S的老路

汪小菲喜得三胎僅2天,荒唐事接連發(fā)生,馬筱梅或走上大S的老路

攬星河的筆記
2026-02-26 19:35:46
2026-03-02 18:20:49
侑虎科技UWA incentive-icons
侑虎科技UWA
游戲/VR性能優(yōu)化平臺
1552文章數(shù) 986關(guān)注度
往期回顧 全部

科技要聞

榮耀發(fā)布機器人手機、折疊屏、人形機器人

頭條要聞

36歲副鎮(zhèn)長開會暈倒除夕當天不幸離世 家中有3個孩子

頭條要聞

36歲副鎮(zhèn)長開會暈倒除夕當天不幸離世 家中有3個孩子

體育要聞

“想要我簽名嗎” 梅西逆轉(zhuǎn)后嘲諷對手主帥

娛樂要聞

美伊以沖突爆發(fā),多位明星被困中東

財經(jīng)要聞

金銀大漲 市場仍在評估沖突會否長期化

汽車要聞

國民SUV再添一員 瑞虎7L靜態(tài)體驗

態(tài)度原創(chuàng)

手機
教育
健康
本地
公開課

手機要聞

vivo韓伯嘯揭秘X300 Ultra手機視頻拍攝升級亮點

教育要聞

2月28日雅思大作文示范寫作 | 違規(guī)駕駛成因與最優(yōu)對策分析

轉(zhuǎn)頭就暈的耳石癥,能開車上班嗎?

本地新聞

津南好·四時總相宜

公開課

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

無障礙瀏覽 進入關(guān)懷版