非华为电脑安装华为电脑管家分析
近期有使用手机投屏的需求,用过几个小工具感觉效果不是很理想,所以想着着手分析下。
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最终效果
