非華為電腦安裝華為電腦管家分析
近期有使用手機投屏的需求,用過幾個小工具感覺效果不是很理想,所以想著著手分析下。
1分析環境
軟件版本 :11.1.6.31 (PCManager_Setup_11.1.6.31(C233D005).exe)
虛擬機 :windows 10 21H2 x64
真機 :windows 10 21H1 x64
工具 :IDA 、VS 2019
測試手機 :華為 Mate 30 5G
2首次安裝運行分析
從華為電腦管家官網(https://consumer.huawei.com/cn/support/pc-manager/)上下載最新版本的安裝包后,給我的感覺是一個類似與msi的安裝包。
將安裝包拖入虛擬機,同時打開process monitor抓取一波安裝時的行為,方便后續分析。
首先最直觀的提示是:

同樣是windows,華為電腦和其友商電腦有什么區別?我感覺最直觀的體現在于下圖:

有這個方向下一步就需要關注下針對主板或者系統信息的一次操作。
3分析安裝包
將安裝包拖入到ida中,在入口函數處竟然發現了一個明顯的提示:

這華為電腦管家安裝包應該是通過NSIS(https://nsis.sourceforge.io/Main_Page)打包生成的。之前使用過NSIS進行過打包,該工具主要通過編寫一個腳本文件(*.nsi/*.ini等)完成一系列操作。例子如下:

用7Z直接解壓PCManager_Setup_11.1.6.31(C233D005).exe看看是否可能從修改腳本重打包的方式繞過對設備的檢測。

但是在壓縮包的根目錄并沒有找到相關的腳本文件,因此這個方法并不可行。
不過既然解壓了就試下解壓后的可執行文件是否能運行。在解壓目錄找到PCManager.exe運行。

可以直接運行,難道就此結束嗎?跟著窗口的各種提示一路點擊"同意",但是還是有問題。

可能是跳過了安裝過程沒有給安裝上服務的原因,點擊"修復"。
連接手機并進入"多屏協同"。此時并不能正常識別到手機。

到此看來,似乎并不能通過解壓的方式正常運行"華為電腦管家"。
4
分析安裝流程中的模塊
知道了安裝包并不包含主要的邏輯代碼,那么就需要從安裝包中包含的模塊入手分析。
回到開始時用process monitor抓取到的信息中,在日志中可以看到一條信息。

通過命令行參數可以大膽猜測下,這里可能就是驗證設備兼容性的函數。在IDA中定位到MBAInstallPre.exe中對參數isSupportDevice判斷的位置。

通過觀察流程該函數Func_isSupportDevice主要有兩個可能的返回值,分別是 : 1 或 2

首先進入函數sub_1400162D0

該函數在入口處調用了一次導入函數之后便進入到字符串的拼接環節。
這里主要看下導入函數的工作流程。



這里多次跟進ProductAdapt::MachineType::GetInstance后發現會進入到一個函數__int64 __fastcall ProductAdapt::MachineType::LoadConfig(ProductAdapt::MachineType *this)
在函數ProductAdapt::MachineType::LoadConfig再次發現了Func_isSupportDevice里用到的一個類實例SmBiosHelper::GetInstance()

看來這個SmBiosHelper這個類才是真正干活的。該函數由HardwareHal.dll導出。

在其初始化函數中發現了如下調用:

下面是函數sub_180032750的主要邏輯:

通過MSDN GetSystemFirmwareTable發現該函數可以讀取到SMBIOS固件表。同時該函數也提到了使用WMI也可以獲取到。
通過GitHub代碼 DumpSMBIOS嘗試獲取數據。

通過wbemtest.exe打開ROOT\WMI并打開類MSSMBios_RawSMBiosTables找到SMBiosData

顯示SMBiosData字段無數據。
這里不清楚為什么通過WMI GUI工具沒有獲取到數據,已經管理員方式運行了。
通過上圖可以看出來使用GetSystemFirmwareTable確實可以拿到主板信息。由上圖中可以知道,如果通過GetSystemFirmwareTable可以拿到信息之后則跳過了通過WMI方式獲取數據。
在拿到SMBiosData字段或者說SMBIOSTableData字段后,程序進行了如下處理。

主要對其中的5個字段進行了解析(文章結尾提供BIOS更多信息)。分別是:
BIOS Information (Type 0)System Information (Type 1)Baseboard (or Module) Information (Type 2)System Enclosure (Type 3)OEM Strings (Type 11)
回到函數sub_1400162D0內部,當函數ProductCheckSupport::GetDeviceTypeEx返回后,則會得到對應設備的GetProductName,通過返回的ProductName與軟件Config中包含的ProductName進行比較。

如果與Config中的ProductName一致,則函數返回1否則返回0
至此函數sub_1400162D0流程分析完畢。下面來看Func_isSupportDevice中的else分支。

else分支直接通過SmBiosHelper::GetSysManufactor來獲取主板制造商。并將字符串轉換為大寫后與HUAWEI和XXXX進行比較。

如果主板廠商是HUAWEI則函數返回1,否則函數返回2。
至此MBAInstallPre.exe中的Func_isSupportDevice函數分析完畢。
5模塊分析總結
通過上面的對其中的MBAInstallPre.exe->isSupportDevice流程的大概分析可以知道,模塊HardwareHal.dll中的類SmBiosHelper會通過函數GetSystemFirmwareTable或者WMI來獲取主板信息。
接下來則通過hook函數GetSystemFirmwareTable處理其返回值。
通過MSDN GetSystemFirmwareTable知道正確的函數調用方式如下:
DWORD error = ERROR_SUCCESS;DWORD smBiosDataSize = 0;RawSMBIOSData* smBiosData = NULL; // Defined in this linkDWORD bytesWritten = 0;
// Query size of SMBIOS data.// 第一次調用時為了獲取SMBIOSData的數據大小smBiosDataSize = GetSystemFirmwareTable('RSMB', 0, NULL, 0);
// Allocate memory for SMBIOS datasmBiosData = (RawSMBIOSData*) HeapAlloc(GetProcessHeap(), 0, smBiosDataSize);if (!smBiosData) { error = ERROR_OUTOFMEMORY; goto exit;}
// Retrieve the SMBIOS table// 第二次調用時為了獲取SMBIOSData的數據
bytesWritten = GetSystemFirmwareTable('RSMB', 0, smBiosData, smBiosDataSize);
if (bytesWritten != smBiosDataSize) { error = ERROR_INVALID_DATA; goto exit;}
// Process the SMBIOS data and free the memory under an exit label
主要代碼如下(修改自GitHub代碼 DumpSMBIOS)。
更多BIOS結構信息在文章結尾提供相關鏈接。
UINT WINAPI Hooked_GetSystemFirmwareTable( _In_ DWORD FirmwareTableProviderSignature, _In_ DWORD FirmwareTableID, _Out_writes_bytes_to_opt_(BufferSize, return) PVOID pFirmwareTableBuffer, _In_ DWORD BufferSize){ PTF_LOG_A("Hooked_GetSystemFirmwareTable."); UINT uRetValue = 0;
uRetValue = g_FUNC_GetSystemFirmwareTable(FirmwareTableProviderSignature, FirmwareTableID, pFirmwareTableBuffer, BufferSize);
if (FirmwareTableProviderSignature != 'RSMB') { PTF_LOG_A("Hooked_GetSystemFirmwareTable. Signature is not \'RSMB\'"); return uRetValue; }
if (pFirmwareTableBuffer != NULL && BufferSize > 0 && uRetValue <= BufferSize) { PTF_LOG_A("Hooked_GetSystemFirmwareTable. Modify Data."); const PRawSMBIOSData pDMIData = (PRawSMBIOSData)pFirmwareTableBuffer; //修改返回數據 DumpSMBIOSStruct(pDMIData, pDMIData->Length); PTF_LOG_A("Hooked_GetSystemFirmwareTable. Modify Data Finish."); } return uRetValue;}
void DumpSMBIOSStruct(void* pAddress, unsigned int Len){ LPBYTE p = (LPBYTE)(pAddress); const LPBYTE lastAddress = p + Len; PSMBIOSHEADER pHeader;
for (;;) { pHeader = (PSMBIOSHEADER)p;
if (ModiySysInfo(pHeader) == true) break;
if ((pHeader->Type == 127) && (pHeader->Length == 4)) break; // last avaiable tables LPBYTE nt = p + pHeader->Length; // point to struct end while (0 != (*nt | *(nt + 1))) nt++; // skip string area nt += 2; if (nt >= lastAddress) break; p = nt; }}
/*ModiySysInfo 函數 為了防止格式識別錯誤,最好是刪除當前System Information節。自己重新構建一個節并添加到全部數據的尾部。同時需要更新GetSystemFirmwareTable返回值的大小。以上前提是提供給GetSystemFirmwareTable的輸出緩沖區足夠長。*/bool ModiySysInfo(PSMBIOSHEADER pHeader){ if (pHeader->Type == 1) { /*https://consumer.huawei.com/cn/support/laptops/matebook-e/*/ PSystemInfo pSystem = (PSystemInfo)pHeader; char* str = (char *)pHeader + pHeader->Length; const char* pszManufacturer = "HUAWEI";//主板廠商 const char* pszProductName = "BLl-W19";//產品名 const char* pszVersion = "1.0";//版本 //https://consumer.huawei.com/cn/support/warranty-query/ //這里的SerialNumber在測試中發現了個小問題 //如果未提供一個可用的SN則不能在軟件中使用某些聯網功能 //如 "玩機技巧" "快捷服務" 等 const char* pszSerialNumber = "ASM51ASMASM51ASM";//16位主板序列號
//獲取原各字段信息 const char* pszOldManufacturer = LocateStringA(str, pSystem->Manufacturer); const char* pszOldProductName = LocateStringA(str, pSystem->ProductName); const char* pszOldVersion = LocateStringA(str, pSystem->Version); const char* pszOldSerialNumber = LocateStringA(str, pSystem->SN);
if ( strlen(pszOldManufacturer) > strlen(pszManufacturer) && strlen(pszOldProductName) > strlen(pszProductName)&& strlen(pszOldVersion) > strlen(pszVersion)&& strlen(pszOldSerialNumber) > strlen(pszSerialNumber) ) { //如果原主板信息足夠長則可以直接修改 PTF_LOG_A("Data length enough."); str = ModiyStringData(str, pszManufacturer); str = ModiyStringData(str, pszProductName); str = ModiyStringData(str, pszVersion); str = ModiyStringData(str, pszSerialNumber); return true; } else { //原主板信息較短,則需要另辟蹊徑 //... } } return false;}
char * ModiyStringData(char* pAddress, const char* pszTargetData){ if (0 == *pAddress) return pAddress; int nTragetLen = strlen(pszTargetData) + 1; strcpy_s(pAddress, nTragetLen, pszTargetData); return (pAddress + nTragetLen);}
6最終效果

