<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    進程隱藏技術

    VSole2021-12-08 16:44:25

    次實現是在WIN7 X86系統上進行,實驗要達到的目的就是實現進程的隱藏,以讓任務管理器查不到要隱藏的進程。這里要隱藏的程序是一個簡單的HelloWord彈窗程序,程序名是demo.exe。

    用戶層的進程隱藏技術

    1、實現原理

    用戶層的進程隱藏的實現主要是通過HOOK任務管理器的ZwQuerySystemInformation函數。之所以是這個函數,是因為無論是通過EnumProcess函數還是CreateToolhelp32Snapshot函數來查詢進程,它們最終都會調用ntdll.dll中的ZwQuerySystemInformation函數來實現功能。

    所以只要采用DLL注入技術,將DLL注入到要HOOK的進程中,并在DLL加載的時候執行HOOK ZwQuerySystemInformation函數就可以實現進程隱藏。關于如何實現DLL注入請參考這篇常見的幾種DLL注入技術。而對ZwQuerySystemInformation的HOOK采取的是Inline Hook的技術。如何實現Inline Hook請參考這篇內核層的三種HOOK技術。

    在IDA中可以看到ZwQuerySystemInformation的實現如下。由于它最開始的五個字節是為eax賦值調用號,所以其實可以根據熱補丁的思想,對這五個字節進行HOOK。然后在HOOK完要執行的函數里面對eax進行重新賦值以后在跳轉到下一行代碼也就是mov edx,0x7FFE0300進行執行。

    由于之前寫過熱補丁技術,這里的話就用傳統的HOOK步驟。

    ① 使用GetProcAddress函數獲取要HOOK的函數的地址,并將其保存。

    ② 修改前五字節的頁屬性為可讀可寫可執行。

    ③ 將這五個字節讀出來備份起來。

    ④ 計算從需要跳轉的大小,公式是:要跳轉的目的地址-(HOOK的函數的地址 + 5)。

    ⑤ 將計算好距離的跳轉指令寫入函數的這五個字節。

    ⑥ 還原頁屬性。

    UnHook則非常簡單:

    ① 判斷函數是否被HOOK。

    ② 修改函數地址頁屬性為可讀可寫可執行。

    ③ HOOK的時候保存的五個字節寫回到函數地址。

    ④ 恢復函數地址頁屬性。

    在完成HOOK以后執行的函數內部就需要以下的步驟來讓程序正常運行:

    ① 首先調用UnHook將函數恢復。

    ② 調用原函數獲取返回結果,將要隱藏的進程隱藏掉。

    ③ 再次對程序進行HOOK操作。

    至于要如何將進程隱藏起來,就需要首先看看ZwQuerySystemInformation在文檔中的定義了。

    NTSTATUS WINAPI ZwQuerySystemInformation(
      __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
      __inout    PVOID SystemInformation,
      __in       ULONG SystemInformationLength,
      __out_opt  PULONG ReturnLength);
    

    參數

    說明

    SystemInformationClass

    要檢索的類型。是一個SYSTEM_INFORMATION_CLASS的聯合體

    SystemInformation

    指向緩沖區的指針,用于接收請求信息。該信息的大小和結構取決于SystemInformationClass

    SystemInformationLength

    SystemInformation參數指向的緩沖區的大小

    ReturnLength

    一個可選指針,指向函數寫入請求信息的實際大小的位置

    而SYSTEM_INFORMATION_CLASS,在文檔中的定義如下:

    typedef enum _SYSTEM_INFORMATION_CLASS {    SystemBasicInformation = 0,    SystemPerformanceInformation = 2,    SystemTimeOfDayInformation = 3,    SystemProcessInformation = 5,    SystemProcessorPerformanceInformation = 8,    SystemInterruptInformation = 23,    SystemExceptionInformation = 33,    SystemRegistryQuotaInformation = 37,    SystemLookasideInformation = 45} SYSTEM_INFORMATION_CLASS;
    

    當它指定為SystemProcessInformation(0x5)的時候,就表示要檢索系統的進程信息。函數將會得到所有的進程信息并把這些得到的進程信息的內容保存到SYSTEM_PROCESS_INFORMATION結構數組,數組中的每一個元素都代表了一個進程信息。而數組的首地址將會保存到第二個參數SystemInformation中。

    而SYSTEM_PROCESS_INFORMATION在文檔中的定義如下:

    typedef struct _SYSTEM_PROCESS_INFORMATION {    ULONG NextEntryOffset;    BYTE Reserved1[52];    PVOID Reserved2[3];    HANDLE UniqueProcessId;    PVOID Reserved3;    ULONG HandleCount;    BYTE Reserved4[4];    PVOID Reserved5[11];    SIZE_T PeakPagefileUsage;    SIZE_T PrivatePageCount;    LARGE_INTEGER Reserved6[6];} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
    

    其中的NextEntryOffset代表的是下一個SYSTEM_PROCESS_INFORMATION元素距離現在這個SYSTEM_PROCESS_INFORMATION元數的偏移。

    而UniqueProcessId就是查詢到的這個進程的PID。根據它就可以找到要隱藏的進程,并將它從這個結構體數組中斷開,也就是要隱藏進程的SYSTEM_PROCESS_INFORMATION的上一個元數的NextEntryOffset加上當前SYSTEM_PROCESS_INFORMATION的NextEntryOffset。具體代碼實現如下:

    // dllmain.cpp : 定義 DLL 應用程序的入口點。#include #include #include #include  #define HIDE_PROCESS_NAME "demo.exe"  //要隱藏的進程名 typedefNTSTATUS(WINAPI* pfnZwQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass,                                      PVOID SystemInformation,                                      ULONG SystemInformationLength,                                      PULONG ReturnLength);  //HOOK以后要執行的函數NTSTATUS WINAPI MyZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass,                                           PVOID SystemInformation,                                           ULONG SystemInformationLength,                                           PULONG ReturnLength);BOOL Hook();BOOL UnHook();VOID ShowError(PCHAR msg);DWORD WINAPI ThreadProc(LPVOID lpParameter);DWORD GetPid(PCHAR pProName); //根據進程名獲取要隱藏的進程的PID DWORD g_dwOrgAddr = 0;   //原函數地址CHAR g_szOrgBytes[5] = { 0 };  //保存函數的前五個字節DWORD g_dwHidePID = 0;   //要隱藏的進程的PID BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved                     ){    switch (ul_reason_for_call)    {        case DLL_PROCESS_ATTACH:        {            HANDLE hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);            if (hThread) CloseHandle(hThread);            break;        }        case DLL_THREAD_ATTACH:        case DLL_THREAD_DETACH:        case DLL_PROCESS_DETACH:            break;    }    return TRUE;} BOOL Hook(){    BOOL bRet = TRUE;    HMODULE hNtDll = NULL;    pfnZwQuerySystemInformation ZwQuerySystemInformation = NULL;    BYTE szShellCode[5] = { 0xE9, 0, 0, 0, 0 };    //寫入跳轉指令的五字節    DWORD dwOldProtect = 0;  //保存原來的頁屬性     hNtDll = LoadLibrary("ntdll.dll");    if (hNtDll == NULL)    {        ShowError("LoadLibrary");        bRet = FALSE;        goto exit;    }     //獲取函數地址    ZwQuerySystemInformation = (pfnZwQuerySystemInformation)GetProcAddress(hNtDll, "ZwQuerySystemInformation");    if (ZwQuerySystemInformation == NULL)    {        ShowError("GetProcAddress");        bRet = FALSE;        goto exit;    }     //保存HOOK函數的地址    g_dwOrgAddr = (DWORD)ZwQuerySystemInformation;     //修改頁屬性是可讀可寫可執行    if (!VirtualProtect(ZwQuerySystemInformation, sizeof(szShellCode), PAGE_EXECUTE_READWRITE, &dwOldProtect))    {        ShowError("VirtualProtect");        bRet = FALSE;        goto exit;    }     //將原來的五個字節內容保存    if (!ReadProcessMemory(GetCurrentProcess(), ZwQuerySystemInformation, g_szOrgBytes, sizeof(g_szOrgBytes), NULL))    {        ShowError("ReadProcessMemory");        bRet = FALSE;        goto exit;    }     //計算要跳轉的長度    *(PDWORD)(szShellCode + 1) = (DWORD)MyZwQuerySystemInformation - ((DWORD)ZwQuerySystemInformation + 5);    //將shellcode寫入    if (!WriteProcessMemory(GetCurrentProcess(), ZwQuerySystemInformation, szShellCode, sizeof(szShellCode), NULL))    {        ShowError("WriteProcessMemory");        bRet = FALSE;        goto exit;    }     //還原頁屬性    if (!VirtualProtect(ZwQuerySystemInformation, sizeof(szShellCode), dwOldProtect, &dwOldProtect))    {        ShowError("VirtualProtect");        bRet = FALSE;        goto exit;    }exit:    return bRet;} BOOL UnHook(){    BOOL bRet = TRUE;    DWORD dwOldProtect = 0;  //保存頁屬性     if (g_dwOrgAddr == 0)    {        MessageBox(NULL, TEXT("函數還未HOOK"), TEXT("Error"), MB_OK);        bRet = FALSE;        goto exit;    }     //修改頁屬性為可讀可寫可執行    if (!VirtualProtect((PVOID)g_dwOrgAddr, sizeof(g_szOrgBytes), PAGE_EXECUTE_READWRITE, &dwOldProtect))    {        ShowError("VirtualProtect");        bRet = FALSE;        goto exit;    }     //將函數中原來的內容恢復回去    if (!WriteProcessMemory(GetCurrentProcess(), (PVOID)g_dwOrgAddr, g_szOrgBytes, sizeof(g_szOrgBytes), NULL))    {        ShowError("WriteProcessMemory");        bRet = FALSE;        goto exit;    }     //將頁屬性恢復    if (!VirtualProtect((PVOID)g_dwOrgAddr, sizeof(g_szOrgBytes), dwOldProtect, &dwOldProtect))    {        ShowError("VirtualProtect");        bRet = FALSE;        goto exit;    }     g_dwOrgAddr = 0;    memset(g_szOrgBytes, 0, sizeof(g_szOrgBytes));exit:    return bRet;}  NTSTATUS WINAPI MyZwQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass,                                            PVOID SystemInformation,                                            ULONG SystemInformationLength,                                            PULONG ReturnLength){    NTSTATUS status = 0;    PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;    DWORD dwOrgFuncAddr = 0;          //獲取函數地址    dwOrgFuncAddr = g_dwOrgAddr;     //卸載HOOK    if (!UnHook())    {        MessageBox(NULL, TEXT("UnHook失敗"), TEXT("Error"), MB_OK);        goto exit;    }     status = ((pfnZwQuerySystemInformation)dwOrgFuncAddr)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);         //判斷函數是否調用成功,以及是否是查詢進程的操作    if (NT_SUCCESS(status) && SystemInformationClass == SystemProcessInformation)    {        pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;        while (TRUE)        {            //判斷是否是要隱藏的進程            if (g_dwHidePID == (DWORD)pCur->UniqueProcessId)            {                //將進程隱藏起來                if (pPrev == NULL)   SystemInformation = (PBYTE)pCur + pCur->NextEntryOffset;                else if (pCur->NextEntryOffset == 0) pPrev->NextEntryOffset = 0;                else pPrev->NextEntryOffset += pCur->NextEntryOffset;                break;            }            else pPrev = pCur;             //如果沒有下一個成功則退出             if (pCur->NextEntryOffset == 0) break;                     //將指針指向下一個成員            pCur = (PSYSTEM_PROCESS_INFORMATION)((PBYTE)pCur + pCur->NextEntryOffset);        }    }    //重新HOOK    if (!Hook()) MessageBox(NULL, TEXT("Hook失敗"), TEXT("Error"), MB_OK);exit:    return status;} DWORD WINAPI ThreadProc(LPVOID lpParameter){    g_dwHidePID = GetPid(HIDE_PROCESS_NAME);     if (g_dwHidePID == 0)    {        MessageBox(NULL, TEXT("沒有找到要隱藏的進程"), TEXT("Error"), MB_OK);    }    else    {        if (!Hook())        {            MessageBox(NULL, TEXT("Hook 失敗"), TEXT("Error"), MB_OK);        }        else MessageBox(NULL, TEXT("Hook成功"), TEXT("Success"), MB_OK);    }      return 0;} DWORD GetPid(PCHAR pProName){    PROCESSENTRY32 pe32 = { 0 };    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    BOOL bRet = FALSE;     if (hSnap == INVALID_HANDLE_VALUE)    {        printf("CreateToolhelp32Snapshot process %d", GetLastError());        return 0;    }     pe32.dwSize = sizeof(pe32);    bRet = Process32First(hSnap, &pe32);    while (bRet)    {        if (lstrcmp(pe32.szExeFile, pProName) == 0)        {            return pe32.th32ProcessID;        }        bRet = Process32Next(hSnap, &pe32);    }     CloseHandle(hSnap);     return 0;} VOID ShowError(PCHAR msg){    CHAR szError[105] = { 0 };     sprintf(szError, "%s Error %d", msg, GetLastError());    MessageBox(NULL, szError, TEXT("Error"), MB_OK);}
    

    2.運行結果

    在實現注入HOOK函數之前可以看到任務管理器可以查看到打開的demo.exe進程。

    完成注入,HOOK成功之后就看不到了。

    內核層的進程隱藏技術

    1、實現原理

    在內核中每一個進程都有對應的一個EPROCESS結構體,在Windows7下這個結構體中的部分成員如下,其中0x16C保存了進程名字的指針。通過這個指針可以獲得當前EPROCESS表示的是哪一個進程。

    3: kd> dt _EPROCESSnt!_EPROCESS   +0x000 Pcb              : _KPROCESS   +0x098 ProcessLock      : _EX_PUSH_LOCK   +0x0a0 CreateTime       : _LARGE_INTEGER   +0x0a8 ExitTime         : _LARGE_INTEGER   +0x0b0 RundownProtect   : _EX_RUNDOWN_REF   +0x0b4 UniqueProcessId  : Ptr32 Void   +0x0b8 ActiveProcessLinks : _LIST_ENTRY    //進程鏈表   +0x0c0 ProcessQuotaUsage : [2] Uint4B   +0x0c8 ProcessQuotaPeak : [2] Uint4B   +0x0d0 CommitCharge     : Uint4B   +0x0d4 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK   +0x0d8 CpuQuotaBlock    : Ptr32 _PS_CPU_QUOTA_BLOCK   +0x0dc PeakVirtualSize  : Uint4B   +0x0e0 VirtualSize      : Uint4B   +0x0e4 SessionProcessLinks : _LIST_ENTRY   +0x0ec DebugPort        : Ptr32 Void   +0x0f0 ExceptionPortData : Ptr32 Void   +0x0f0 ExceptionPortValue : Uint4B   +0x0f0 ExceptionPortState : Pos 0, 3 Bits   +0x0f4 ObjectTable      : Ptr32 _HANDLE_TABLE   +0x0f8 Token            : _EX_FAST_REF   +0x0fc WorkingSetPage   : Uint4B   +0x100 AddressCreationLock : _EX_PUSH_LOCK   +0x104 RotateInProgress : Ptr32 _ETHREAD   +0x108 ForkInProgress   : Ptr32 _ETHREAD   +0x10c HardwareTrigger  : Uint4B   +0x110 PhysicalVadRoot  : Ptr32 _MM_AVL_TABLE   +0x114 CloneRoot        : Ptr32 Void   +0x118 NumberOfPrivatePages : Uint4B   +0x11c NumberOfLockedPages : Uint4B   +0x120 Win32Process     : Ptr32 Void   +0x124 Job              : Ptr32 _EJOB   +0x128 SectionObject    : Ptr32 Void   +0x12c SectionBaseAddress : Ptr32 Void   +0x130 Cookie           : Uint4B   +0x134 Spare8           : Uint4B   +0x138 WorkingSetWatch  : Ptr32 _PAGEFAULT_HISTORY   +0x13c Win32WindowStation : Ptr32 Void   +0x140 InheritedFromUniqueProcessId : Ptr32 Void   +0x144 LdtInformation   : Ptr32 Void   +0x148 VdmObjects       : Ptr32 Void   +0x14c ConsoleHostProcess : Uint4B   +0x150 DeviceMap        : Ptr32 Void   +0x154 EtwDataSource    : Ptr32 Void   +0x158 FreeTebHint      : Ptr32 Void   +0x160 PageDirectoryPte : _HARDWARE_PTE   +0x160 Filler           : Uint8B   +0x168 Session          : Ptr32 Void   +0x16c ImageFileName    : [15] UChar    //指向進程的名稱   +0x17b PriorityClass    : UChar   +0x17c JobLinks         : _LIST_ENTRY   +0x184 LockedPagesList  : Ptr32 Void   +0x188 ThreadListHead   : _LIST_ENTRY
    

    其中偏移0xB8的ActiveProcesssLinks是一個LIST_ENTRY的鏈表,它在文檔中的定義如下:

    typedef struct _LIST_ENTRY {   struct _LIST_ENTRY *Flink;    //指向下一個EPROCESS的ActiveProcessLinks   struct _LIST_ENTRY *Blink;    //指向上一個EPROCESS的ActiveProcessLinks} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
    

    這是一個雙向鏈表,通過這個鏈表就可以遍歷系統中的所有進程。而用戶層通過API查看進程的時候,就是通過這個鏈表來查找進程的內容。所以只要在內核中將相應進程從這個鏈表中斷鏈就可以實現進程隱藏。但是要注意這個鏈表的中的成員指向的是另一個EPROCESS的ActiveProcessLinks,如下圖所示。所以要獲得這個進程的EPROCESS還需要減去0xB8。

    具體實現的代碼如下:

    VOID HideProcess(){    PEPROCESS pCurPro = NULL, pPrevPro = NULL;    PCHAR pImageFileName = NULL;    PLIST_ENTRY pListEntry = NULL;     //獲取當前進程的EPROCESS    pCurPro = PsGetCurrentProcess();    pPrevPro = pCurPro;    do     {        //獲取EPROCESS的進程名        pImageFileName = (PCHAR)pCurPro + 0x16C;        //是否是要隱藏的進程        if (strcmp(pImageFileName, "demo.exe") == 0)        {            //對進程進行斷鏈操作            pListEntry = (PLIST_ENTRY)((ULONG)pCurPro + 0xB8);            pListEntry->Blink->Flink = pListEntry->Flink;            pListEntry->Flink->Blink = pListEntry->Blink;            DbgPrint("進程%s隱藏成功\r", pImageFileName);        }        pCurPro = (PEPROCESS)(*(PULONG)((ULONG)pCurPro + 0xB8) - 0xB8);    } while (pCurPro != pPrevPro);}
    

    2、運行結果

    驅動加載前可以看到任務管理器可以正常查到運行的demo.exe進程。

    而當驅動啟動,執行了隱藏進程代碼以后,在任務管理器中就看不到demo.exe了。

    dword
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    EXP編寫學習之繞過GS
    2023-02-20 09:58:16
    棧中的守護天使 :GSGS原理向棧內壓入一個隨機的DWORD值,這個隨機數被稱為canary ,IDA稱為 Security Cookie。Security Cookie 放入 ebp前,并且data節中存放一個 Security Cookie的副本。棧中發生溢出時,Security Cookie首先被淹沒,之后才是ebp和返回地址。函數返回之前,會添加一個Security Cookie驗證操作,稱為Security Check。檢測到溢出時,系統將進入異常處理流程,函數不會正常返回,ret也不會被執行。函數使用無保護的關鍵字標記。緩沖區不是8字節類型 且 大小不大于4個字節。可以為函數強制啟用GS。
    0x01 進程遍歷因為進程是在隨時進行變動的所以我們需要獲取一張快照1.1 CreateToolhelp32
    MITM Fuzz下圖是用戶層與內核層實現通信的過程,可以看到,最后是通過NtDeviceIoControlFile來分發給相應驅動對象的派遣函數的,因此,可以通過對該函數進行HOOK操作。如果將修改以后的數據發送給NtDeviceIoControlFile函數以后,發生了內核崩潰或藍屏,往往預示著該驅動程序可能存在內核漏洞。
    windows下利用cmd開啟3389端口
    Hex Comparison注冊分析
    2023-10-12 10:49:49
    在學習Flexlm ECC patch過程中,需要對比下別人patch的exe和原本exe的差異,使用到這款Hex Comparison這款二進制比較工具。那么就先來學習一下這款軟件的注冊機制。
    如果成功連接到管道, 使用WriteFile函數將shellcode數據寫入已連接的命名管道。使用IDA分析檢查導出函數:第一個是反射式DLL注入,限于篇幅,在本篇中不展開分析第二個是DLL的入口函數。在后續的文章中,會根據檢測特征和csprofile的繞過按照專題進行分析。同時,大部分的CS檢測特征都是在此。而在后續的文章分析中,將會著重的分析CS beacon中使用到的幾種DLL注入方式;beacon config的檢測;同時結合CS profile 的配置,研究一下CS的對抗檢測方式。
    x32TLS回調函數實驗
    2023-05-31 09:34:55
    TLS回調函數介紹TLS回調函數是在程序運行時由操作系統自動調用的一組函數,用于在進程加載和卸載時執行一些初始化和清理操作。在TLS回調函數中,可以訪問當前線程的TLS數據,并對其進行修改或檢查。值得一提的是TLS回調可以用來反調試,原理實為在實際的入口點代碼執行之前執行檢測調試器代碼。為了棧平衡,我們要把傳進這個回調函數的參數所占用的
    進來之后,我們就可以在攻擊機上面控制win7靶機了,先查看開放的端口有沒有3389.3、命令行打開3389遠程連接端口netstat -an. 顯示successful,再次確認查看遠程連接端口是否開啟:netstat -an|find "3389". 現在分別講講各自的用法吧:5.1、xfreerdp工具①首先是xfreerdp的,命令格式如下:很容易理解,/u就是用戶名,/p是密碼,/v是靶機(目標)的地址 /size就是圖形化界面的大小而已。輸入y按下回車,等待界面出現5.2、xfreerdp工具②然后是rdesktop,命令格式很簡單:rdesktop 192.168.25.132
    概述在windows系統上,涉及到內核對象的功能函數,都需要從應用層權限轉換到內核層權限,然后再執行想要的內核函數,最終將函數結果返回給應用層。本文就是用OpenProcess函數來觀察函數從應用層到內核層的整體調用流程。OpenProcess函數,根據指定的進程ID,返回進程句柄。NTSTATUS Status; //保存函數執行狀態。OBJECT_ATTRIBUTES Obja; //待打開對象的對象屬性。HANDLE Handle; //存儲打開的句柄。CLIENT_ID ClientId; //進程、線程ID. dwDesiredAccess, //預打開進程并獲取對應的權限。ObjectNamePresent = ARGUMENT_PRESENT ; //判斷對象名稱是否為空
    可在其中找受影響的版本復現,在受影響版本的系統中找到win32k.sys導入IDA。漏洞函數位于win32k.sys的SetImeInfoEx()函數,該函數在使用一個內核對象的字段之前并沒有進行是否為空的判斷,當該值為空時,函數直接讀取零地址內存。如果在當前進程環境中沒有映射零頁面,該函數將觸發頁面錯誤異常,導致系統藍屏發生。tagWINDOWSTATIONspklList對象的結構為:漏洞觸發驗證查看SSDT表dd KeServiceDescriptorTabledds Address L11C 顯示地址里面值指向的地址. 以4個字節顯示。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类