sponsored links

吹爆了!徹底解決隱私方法呼叫,防止App被下架

作者:藍師傅

一、前言

工信部對於App索權問題越來越重視,先後多個大廠App被下架要求整改:

吹爆了!徹底解決隱私方法呼叫,防止App被下架

其中最關鍵的問題是使用者同意隱私協議之前,不能有收集使用者隱私資訊的行為,例如獲取deviceId、androidId等資訊,除此之外,對於頻繁申請許可權、超範圍申請許可權也是需要注意的。

除了開迭代針對性整改,從技術角度思考,有沒有一勞永逸的辦法,杜絕隱私呼叫不合規問題呢?

這就是這篇文章要介紹的方案, 前期透過執行時hook技術高效檢測隱私方法呼叫,

後期透過Gradle Plugin+Transform+ASM 來hook並替換隱私方法呼叫,管控App和第三方SDK的隱私行為,徹底解決隱私不合規問題。

二、執行時hook技術

在隱私整改前期,透過上傳apk到史賓格平臺,然後平臺會安裝apk並執行,就能動態監測隱私方法呼叫,如下圖:

吹爆了!徹底解決隱私方法呼叫,防止App被下架

完成整個流程,打包-上傳-檢測,少說也要50分鐘~

關於隱私行為實時監控,實現原理無非是利用執行時hook技術,記錄方法呼叫資訊。

理論上我們也可以使用執行時hook技術,實現線下快速檢測隱私方法呼叫以及獲取呼叫堆疊的功能。

那麼執行時hook技術有哪些呢?

2.1 Xposed

如果你對Xposed比較熟悉,並且手頭有個root的裝置安裝了Xposed框架,那麼直接開發一個Xposed模組來hook指定方法就可以了。

關於Xposed的原始碼分析感興趣可以參考這一篇文章:抱歉,Xposed真的可以為所欲為——終 · 庖丁解碼,作者有一系列Xposed文章。

由於我的測試裝置是有root許可權的,Xposed方案對我來說難度不大,不過對於普通使用者,有沒有免root的方式呢?

有的~

2.2 VirtualXposed

VirtualXposed 是基於VirtualApp 和 epic 在非ROOT環境下執行Xposed模組的實現(支援5.0~10.0)。

VirtualXposed其實就是一個支援Xposed的虛擬機器,我們把開發好的Xposed模組和對應需要hook的App安裝上去就能實現hook功能。

由於VirtualApp 2017年就閉源轉商業,開源版存在不少問題,而且由於其hook大量系統的函式,所以存在不少相容性問題,有些App安裝之後可能打不開,所以如果手頭的裝置剛好遇到相容性問題,那可以考慮換個手機啦~

2.3 epic

阿里2014年開源了Dexposed 專案,它能夠在Dalvik虛擬機器上無侵入地實現執行時方法攔截,

但是Android 5.0開始使用ART虛擬機器後,不支援ART的Dexposed 就淪為歷史。

之後維術大佬在ART上重新實現了Dexposed,有著與Dexposed完全相同的能力和API,專案地址是epic 。

所以如果不想折騰 Xposed 或者 VirtualXposed,只要在應用內接入epic,就可以實現應用內Xposed hook功能,滿足執行hook需求。

2.3.1 epic 原理:

原理是透過修改ArtMethod的入口函式,把入口函式的前8個位元組修改為一段跳轉指令,跳轉到執行hook操作的函式,原理跟阿里的熱修復框架AndFix差不多,如下圖所示。

吹爆了!徹底解決隱私方法呼叫,防止App被下架

詳細原理可以看原文: 我為Dexposed續一秒——論ART上執行時 Method AOP實現

2.3.2 基於epic 實現一個可配置的執行時hook框架

  1. 讀取配置:
        val inputStream = context.resources.assets.open("privacy_methods.json")
        val reader = BufferedReader(InputStreamReader(inputStream))
        val result = StringBuilder()
        var line: String? = ""
        while (reader.readLine().also { line = it } != null) {
            result.append(line)
        }

        val configEntity = Gson().fromJson(result.toString(), PrivacyMethod::class.java)
        configEntity.methods.forEach {
            hookPrivacyMethod(it)
        }
  1. json配置如下,放在assets目錄:
{
  "methods": [
    {
      "name_regex": "android.app.ActivityManager.getRunningAppProcesses",
      "message": "讀取當前執行應用程序"
    },
    {
      "name_regex": "android.telephony.TelephonyManager.listen",
      "message": "監聽呼入電話資訊"
    },
    ...
  ]
}
  1. 根據讀取的配置,進行hook

    private fun hookPrivacyMethod(entity: PrivacyMethodData) {
        if (entity.name_regex.isNotEmpty()) {
            val methodName = entity.name_regex.substring(entity.name_regex.lastIndexOf(".") + 1)
            val className = entity.name_regex.substring(0, entity.name_regex.lastIndexOf("."))
            try {
                val lintClass = Class.forName(className)
                DexposedBridge.hookAllMethods(lintClass, methodName, object : XC_MethodHook() {
                    override fun beforeHookedMethod(param: XC_MethodHook.MethodHookParam?) {
                        super.beforeHookedMethod(param)

                        Log.i(TAG, "beforeHookedMethod $className.$methodName")
                        Log.d(TAG, "stack= " + Log.getStackTraceString(Throwable()))
                    }
                })
            } catch (e: Exception) {
                Log.w(TAG, "hookPrivacyMethod:$className.$methodName,e=${e.message}")
            }
        }
    }
  1. 執行效果如下:

吹爆了!徹底解決隱私方法呼叫,防止App被下架

如圖所示,執行時輸出隱私方法呼叫堆疊的功能基本實現了,支援透過json配置需要hook的方法。

tip:epic 存在相容性問題,例如Android 11 只支援64位App,所以建議只在debug環境使用。

三、編譯時hook技術

使用epic只解決了驗證隱私方法呼叫問題,針對如下問題無能為力:

  1. release環境如何監控隱私方法呼叫?
  2. 如何管控第三方SDK頻繁呼叫隱私方法問題?

對於這兩個問題,可以使用編譯時hook技術來解決。

說到編譯時hook,首先需要了解編譯流程

3.1 編譯流程

我們使用Android Studio開發,使用Gradle 編譯工具,對於apk編譯流程大家應該都知道,如下圖:

吹爆了!徹底解決隱私方法呼叫,防止App被下架

apk編譯流程無非就是以下這些大的步驟:
1.打包資原始檔,生成R.java檔案
2.將AIDL檔案編譯成java檔案
3.將java檔案透過javac命令編譯成.class檔案
4.將class檔案打包成dex檔案
5.透過apkbuilder工具將dex檔案和資原始檔打包成apk
6.apk簽名
7.apk對齊(可以沒有這一步)

其中第四步(將class檔案打包成dex檔案),中間就涉及到Gradle的一個Transform流程

3.2 瞭解Transform

Transform原理圖如下所示

吹爆了!徹底解決隱私方法呼叫,防止App被下架

將class檔案、jar檔案、資原始檔作為輸入,經過一系列的Transform處理,

首先是自定義的Transform處理,然後是系統的Transform處理,最後一個Transform是負責生成dex檔案。

相關原始碼可以看TaskManager的 createPostCompilationTasks方法,編譯流程原始碼都在這裡面~

吹爆了!徹底解決隱私方法呼叫,防止App被下架

截圖只是貼了自定義Transform的原始碼,後面還有系統的Transform,例如 appliesCustomClassTransforms,用於Profile外掛底層實現。

Transform是跟taskFactory關聯的,可以這樣理解,一個Transform對應Gradle的一個Task

知道了Transform的大概原理,我們可以透過自定義Plugin,註冊一個自定義的Transform到編譯流程中去,目的是拿到所有.class檔案,再結合ASM 工具修改位元組碼。

自定義Gradle Plugin,註冊Transform,程式碼如下所示

class Plugin : Plugin<Project> {

    override fun apply(project: Project) {

      if (project.plugins.hasPlugin("com.android.application")) {
          val extension = project.extensions.getByName("android") as AppExtension
          extension.registerTransform(CommonTransform(project))
      }
    }
}

想要理解為什麼自定義外掛要這麼寫,可以看App編譯外掛原始碼AppPlugin

吹爆了!徹底解決隱私方法呼叫,防止App被下架

建立AppExtension,name是android,最終是儲存到ExtensionsStorage類裡面的一個叫extensions的LinkedHashMap變數裡面,大家感興趣可以去看原始碼。

前面的eproject.extensions.getByName,最終就是從LinkedHashMap中讀取的。

拿到.class檔案之後,怎麼修改呢?這就涉及到修改位元組碼方案選型。

3.3 位元組碼修改框架選擇

目前主流的位元組碼修改框架除了ASM,還有Javaassist,兩者對比:

吹爆了!徹底解決隱私方法呼叫,防止App被下架

由於專案對效能、包體積方面要求比較高,所以無疑採用ASM方案比較合適。

3.4 瞭解ASM框架

我們透過自定義Transform 能拿到.class檔案,之後的位元組碼處理就透過ASM工具,關於ASM的使用就不介紹了,大家可以參考:

Android 中看似高大上的位元組碼修改,這樣學就對了!。

Gradle Plugin + Transform ,這套框架的搭建基本都是模板程式碼,為了節約時間成本和試錯成本,本文直接參考dokit,採用booster api作為外掛的底層實現,booster遮蔽了不同Gradle版本api的差異。

說了那麼多,最重要的還是要看方案設計~

四、初級hook方案

上一步我們透過自定義Transform可以拿到所有.class檔案,後面只要透過ClassVistor和MethodVistor,可以分別拿到每個類和方法的位元組碼,

以 ActivityManager#getRunningAppProcesses 為例,我們要替換成 PrivacyUtil#getRunningAppProcesses,流程圖如下:

吹爆了!徹底解決隱私方法呼叫,防止App被下架

核心hook程式碼如下所示:

        classNode.methods.forEach { method ->
            method.instructions?.iterator()?.forEach { insnNode ->

                if (insnNode is MethodInsnNode) {

                    //命中方法,替換
                    if (insnNode.desc  == "android/app/ActivityManager.getRunningAppProcesses ()Ljava/util/List;" &&
                        insnNode.name == "getRunningAppProcesses" &&
                        insnNode.opcode == Opcodes.INVOKESPECIAL
                    ) {
                        //方法指令替換
                        insnNode.opcode = Opcodes.INVOKESTATIC
                        //呼叫類替換
                        insnNode.owner = "com/lanshifu/asm_plugin_library/privacy/PrivacyUtil"
                        //方法名替換
                        insnNode.name = "getRunningAppProcesses"
                        //引數替換
                        insnNode.desc = "com/lanshifu/asm_plugin_library/privacy/PrivacyUtil.getRunningAppProcesses (Landroid/app/ActivityManager;)Ljava/util/List;"

                    }
                }
            }
        }

解釋:

透過遍歷每個方法的位元組碼指令,判斷是ActivityManager.getRunningAppProcesses這個方法呼叫,就替換成PrivacyUtil#getRunningAppProcesses呼叫,涉及到的位元組碼操作是比較基礎的。

tip:為什麼要遍歷每個方法的位元組碼指令?因為需要hook的方法是系統的方法,沒有被打包到apk中, 單純遍歷方法名是找不到的,必須遍歷每個方法裡面呼叫的位元組碼指令。

到此我們初級版本的編譯時隱私方法hook功能就實現了,但是存在幾個問題:

1、硬編碼,不好維護,增加hook方法比較麻煩;

2、對工具類 PrivacyUtil 有依賴,如果後面其它工程使用了這個外掛,但是沒有引入PrivacyUtil,或者後面外掛升級,PrivacyUtil沒升級,就會報Class Not Found Exception;

3、開發需要熟悉 ASM 位元組碼,每次新增一個隱私方法 hook 都需要對比前後位元組碼變化進行修改驗證,麻煩得很;

五、進階方案

想要解決初級方案存在的三個問題,關鍵在於實現”可配置“,

需要在編譯期能夠讀取hook配置,用註解會比較合適。

進階方案思路如下:

  • 用第一個Transform來收集註解資訊,生成一份hook配置;
  • 用第二個Transform來讀取hook配置,替換隱私方法。

5.1 自定義註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface AsmMethodReplace {
    Class oriClass();

    String oriMethod() default "";

    int oriAccess() default AsmMethodOpcodes.INVOKESTATIC;
}

註解是對方法生效,需要知道需要hook的方法的類名、方法名、方法型別(靜態方法/成員方法)

5.2 註解處理,生成配置

替換一個方法,我們需要的配置如下:

原方法資訊(替換前):oriClass、oriMethod、oriAccess、oriDesc

目標方法資訊(替換後):targetClass、targetMethod、targetAcces、targetDesc

目標方法資訊我們透過ClassNode就能拿到,但是原方法資訊,都放到AsmMethodReplace 註解上就不太合適了,因為oriDesc寫起來比較麻煩, 所以這裡約定好一個註解使用規則,然後oriDesc在程式碼裡讀取就行了。

規則如下:

  • 對於hook靜態方法,註解的方法的引數保持跟原方法一致
  • 對於hook成員方法,註解的方法的第一個引數是Class物件,之後的引數跟原方法保持一致

然後oriDesc就透過targetDesc減去第一個引數計算得出。

例如:
targetDesc=(Landroid/telephony/TelephonyManager;)Ljava/lang/String;
透過字串擷取後得到:
oriDesc= Ljava/lang/String;

舉個

5.2.1 例子1:hook成員方法

假如要替換掉ActivityManager的getRunningAppProcesses方法

    public List<RunningAppProcessInfo> getRunningAppProcesses() {
        try {
            return getService().getRunningAppProcesses();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

由於這個是成員方法,那麼註解的寫法如下:

    @JvmStatic
    @AsmMethodReplace(oriClass = ActivityManager::class, oriAccess = AsmMethodOpcodes.INVOKEVIRTUAL)
    fun getRunningAppProcesses(manager: ActivityManager): List<RunningAppProcessInfo?> {
        //hook 處理
    }

5.2.2 例子2:hook靜態方法

假如要替換掉Settings.System的getString方法

public static String getString(ContentResolver resolver, String name) {
        return getStringForUser(resolver, name, resolver.getUserId());
}

由於是靜態方法,那麼註解的寫法如下:

    @JvmStatic
    @AsmMethodReplace(oriClass = Settings.System::class, oriAccess = AsmMethodOpcodes.INVOKESTATIC)
    fun getString(resolver: ContentResolver, name: String): String? {
        //處理AndroidId
        if (Settings.Secure.ANDROID_ID == name) {
        }
        return Settings.System.getString(resolver, name)
    }

詳細可以參考文末的原始碼。

5.3 流程圖

吹爆了!徹底解決隱私方法呼叫,防止App被下架

最終的流程如上,應該比較清晰了吧~

5.4 注意事項

ASM hook 需要有跡可循,必須明確位元組碼修改的地方,可以列印log,可以儲存記錄到檔案中,如果出現問題可以從hook日誌中排查。

吹爆了!徹底解決隱私方法呼叫,防止App被下架

5.5 小結

進階方案主要做了這幾件事:

  1. 用一個註解處理的Transform,編譯期收集自定義註解資訊,生成一份hook配;
  2. 用另一個Transform,讀取hook配置,hook對應方法;
  3. 隱私方法hook之後,增加快取,解決SDK頻繁讀取隱私資訊問題;
  4. 在使用者沒有同意隱私協議之前,如果呼叫隱私方法,可以給toast提示,並列印呼叫堆疊,如下所示,問題一目瞭然。

吹爆了!徹底解決隱私方法呼叫,防止App被下架

六、其它

目前大廠也有一些開源的編譯時插樁的庫,例如餓了麼開源的lancet,原理也是 Gradle Plugin+Transform+ASM。

如果想深入學習位元組碼插樁,推薦滴滴開源的dokit,裡面有好多位元組碼操作可以學習,例如大圖監控,網路監控等等。

由於Gradle 版本更新比較快,大家最好是在專案中嘗試自己搭建編譯時hook基礎框架,這樣出問題的話,自己比較好解決,同時也能提升自己位元組碼開發的技術。

七、總結

本文從工信部隱私合規要求作為切入點,大概介紹瞭如下知識點:

  1. 執行時hook框架介紹和應用
  2. epic使用和原理
  3. 編譯時hook框架
  4. 從apk編譯流程介紹Transform的原理和應用
  5. 編譯時hook方案對比
  6. 最終實現可配置的編譯時方法替換方案,徹底解決隱私方法呼叫不合規問題

本文難度其實不算非常大,主要是把Gradle外掛和位元組碼修改的整個流程串起來,涉及到的技術基本都有所提及,最終搭建了一個編譯時方法hook框架,之後可以基於這個hook框架做很多東西,例如慢方法檢測、全埋點、監控執行緒呼叫等~

最後

在這裡就還分享一份由大佬親自收錄整理的學習PDF+架構影片+面試文件+原始碼筆記高階架構技術進階腦圖、Android開發面試專題資料,高階進階架構資料

這些都是我現在閒暇時還會反覆翻閱的精品資料。裡面對近幾年的大廠面試高頻知識點都有詳細的講解。相信可以有效地幫助大家掌握知識、理解原理,幫助大家在未來取得一份不錯的答卷。

當然,你也可以拿去查漏補缺,提升自身的競爭力。

真心希望可以幫助到大家,Android路漫漫,共勉!

如果你有需要的話,只需私信我【進階】即可獲取

吹爆了!徹底解決隱私方法呼叫,防止App被下架

吹爆了!徹底解決隱私方法呼叫,防止App被下架

吹爆了!徹底解決隱私方法呼叫,防止App被下架

分類: 數碼
時間: 2022-01-19

相關文章

扒一扒二戰日軍曾使用的那些坦克裝甲車

扒一扒二戰日軍曾使用的那些坦克裝甲車
二戰期間的日本軍隊,主要發展重心是在海空軍上,再加上地理位置的原因,陸軍的發展得不到重視,作戰理念比較老舊,甚至還在沿用一戰期間英國陸軍的作戰模式,同時在坦克裝甲車等陸戰裝備上,發展得也比較緩慢,當時 ...

北宋九帝 今天來扒一扒

北宋九帝 今天來扒一扒
北宋(960年-1127年),終結五代十國戰亂的朝代,傳九位皇帝,享國167年.與南宋合稱宋朝,又稱兩宋,因皇室姓趙,也稱趙宋.北宋九位皇帝,他們有武功強,有的文治厲害.有沒有文武雙全的呢?今天我們就 ...

熱得發燙!紅杉高瓴、騰訊阿里扎堆鉅額投資,小小的健康保險賽道有多大?
<科創板日報>(特約記者 陳夢婕)訊,隨著疫情的爆發,大眾對於健康的意識越來越強. 在此背景下,圍繞醫.藥和健康險的創新公司,成為創投機構們下注的物件.有資料顯示,自去年開始,健康險領域的 ...

夏天手腳心發燙 多因陰虛內熱
40歲的鄭女士一到夏天就明顯感覺到手心.腳心發燙,總想光腳走在瓷磚上,還總感覺內心煩躁.鄭女士很納悶,自己這是什麼毛病? 其實,手腳心發熱這種情況並不少見.有的人夏季手心特別熱,到了冬季反而手心發冷. ...

熱得發燙!紅杉高瓴、騰訊阿里扎堆,小小的健康保險賽道有多大?
隨著疫情的爆發,大眾對於健康的意識越來越強. 在此背景下,圍繞醫.藥和健康險的創新公司,成為創投機構們下注的物件.有資料顯示,自去年開始,健康險領域的融資就未間斷,無論是融資頻率和融資金額都在節節攀升 ...

扒一扒傳說中令人上癮的止痛神藥“頭痛粉”

扒一扒傳說中令人上癮的止痛神藥“頭痛粉”
年紀偏大一點的朋友對"頭痛粉"應該說是很熟悉的,頭痛發熱來一包,效果快又便宜,堪稱"神藥"也!但也經常會聽說有人會上癮,不吃就不行了,渾身難受,結果越吃越多,聽 ...

手機發熱發燙嚴重?搞清楚這5個原因,永久擺脫手機發燙問題

手機發熱發燙嚴重?搞清楚這5個原因,永久擺脫手機發燙問題
相信大家在身邊都會有遇到過使用手機的時間太長然後導致手機發熱太燙的情況,那麼我們在使用手機的時候遇到這種情況應該怎麼樣去避免呢?導致發燙發熱太嚴重又是哪幾種原因導致的呢?接下來,筆者就為你介紹一下這五 ...

全連只剩7人,槍管發燙打不出子彈,生死之際號兵大喊:我來指揮

全連只剩7人,槍管發燙打不出子彈,生死之際號兵大喊:我來指揮
所謂張飛當陽橋頭一喝,震退曹操數十萬大軍,體現了個人英雄主義,但其中更多的是藝術加工的結果. 特別是到了現代,個人英雄勇武並不足以以一敵眾了,現代武器的加持下使得個人之間的差距無限被抹平. 但在我國的 ...

扒一扒我國各衛視暱稱是什麼,有芒果臺,還有龍蝦臺

扒一扒我國各衛視暱稱是什麼,有芒果臺,還有龍蝦臺
小時候,由於當時條件有限,很多地方也只能收看到幾個頻道的節目,不過,看電視依然是每天茶餘飯後的最大樂趣,電視臺標形狀也成為我們深刻的記憶.後來,閉路電視和機頂盒的普及,基本上全國的衛影片道都能收到了, ...

扒一扒宋徽宗

扒一扒宋徽宗
我認識宋徽宗是從巜水滸傳>開始的,巜水滸傳>開篇寫到高俅,引出端王(即位後為宋徽宗),這樣描述:"端王......乃神宗天子第十一子,排號九大天王,是個聰明俊俏人物,這浮郎子弟, ...

瞭解手機久用發燙的原因!幾個小妙招,保護手機不再發燙

瞭解手機久用發燙的原因!幾個小妙招,保護手機不再發燙
小夥伴有沒有覺得手機在使用過程中經常會出現發熱發燙的現象,特別是在玩遊戲.刷影片.長時間通話過程中,手機特別燙.尤其是如果在夏季,機身發燙會給人一種彷彿下一秒接下來就要爆炸的感覺. 那麼,手機用久了為 ...

扒一扒那些烏龜的常見病怎麼處理治療(1)

扒一扒那些烏龜的常見病怎麼處理治療(1)
一般來說,寵物小烏龜常見的病有:白眼病,腐皮,腐甲,四肢浮腫,肺炎等,如果你覺得有幫助的話記得點贊關注,我後期會繼續更新其他病的治療方法.今天我們就聊一聊白眼病的引起和治療方法,那麼白眼病是如何產生的 ...

手機用一段時間就發燙?注意這幾點,快速解決
時常有朋友反映,自己的手機總是陷入"高溫"狀態."像拿著一塊燒著的炭一樣,我都擔心自己被灼傷". 給手機降溫, 幾個超實用的技巧碼一下. 減少程式執行功耗 當執 ...

扒一扒阿里商業帝國現金流到底有多麼的恐怖?

扒一扒阿里商業帝國現金流到底有多麼的恐怖?
今天我們按照天貓商城的入駐保證金標準來計算,入駐天貓保證金: 旗艦店:10萬 專營店:15萬 專賣店:5萬 再看一下三種類型的店鋪各有多少家: 旗艦店總計:395280家(三十九萬五千二百八十) 專營 ...

iPhone13快充會發燙?浦記這條MFi快充線堪稱最佳方案

iPhone13快充會發燙?浦記這條MFi快充線堪稱最佳方案
iPhone13釋出後,充電器的選擇十分多,但線材一開始只有原廠蘋果C91 USB-C to Lightning可選,價格並不友好.自從蘋果給第三方品牌開放C94 USB-C to Lightning ...

“好貨也要勤吆喝”扒一扒駁殼槍的各國經銷商們(二)

“好貨也要勤吆喝”扒一扒駁殼槍的各國經銷商們(二)
(接上一期)美國是槍支使用大國,槍文化濃厚,有廣袤的民用市場,毛瑟兵工廠自然不會放棄.但美國自家就有很多著名的兵工廠,要想打入美國市場那就還得找美國人幫忙吆喝銷售產品.馮·倫格克和德特莫德公司成為毛瑟 ...

扒一扒!詹姆斯究竟與多少位03屆新秀做過隊友?

扒一扒!詹姆斯究竟與多少位03屆新秀做過隊友?
隨著卡梅隆-安東尼在這個夏天選擇加盟湖人,03一代兩位一時瑜亮的天之驕子終於在生涯末期聯手了,算上已經退役的波什和韋德,詹姆斯也算與同屆的幾位頂級球星都做過隊友了.但是,大家有沒有想過這樣一個問題,詹 ...

陳德容、黃奕、孫菲菲,扒一扒那些出道驚豔但很快長殘的美女們

陳德容、黃奕、孫菲菲,扒一扒那些出道驚豔但很快長殘的美女們
美貌就是娛樂圈的通行證. 有的美人受時光厚待,並不會急於老去,一直美麗優雅.但是有的美人只讓人驚鴻一瞥,曇花一現,經不起歲月的侵蝕. 接下來我們看一下那些出道時驚豔四座,但長相卻很快偏離軌道的美女們吧 ...

扒一扒那些好用不貴的經典國貨!90後的都見過

扒一扒那些好用不貴的經典國貨!90後的都見過
現在市面上的護膚產品越來越多,包裝也讓人眼花繚亂,但是否真的像廣告所說的那麼好我們也不知道,但是護膚屆有這樣一股"清流",它們包裝簡陋,價格便宜,隱匿在年代久遠的日化店裡,只有老一 ...

韓國為何會將首都由漢城改為首爾?帶您扒一扒首爾名稱的由來

韓國為何會將首都由漢城改為首爾?帶您扒一扒首爾名稱的由來
我們都知道,1988年的奧運會是漢城奧運會,那個時候韓國的首都還不叫首爾,而是漢城,那麼為什麼後來韓國會在2005年將首都從漢城改成首爾呢?很多人認為,漢城在韓語中本身就讀作首爾(seoul),用音譯 ...