本文为看雪论坛精华文章
看雪论坛作者ID:shinratensei
分析环境
软件版本 :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
首次安装分析
从华为电脑管家官网(https://consumer.huawei.com/cn/support/pc-manager/)上下载最新版本的安装包后,给我的感觉是一个类似与msi的安装包。将安装包拖入虚拟机,同时打开process monitor抓取一波安装时的行为,方便后续分析。
首先打开,出现:
分析安装包
将安装包拖入到ida中,在入口函数处竟然发现了一个明显的提示:
这华为电脑管家安装包应该是通过NSIS(https://nsis.sourceforge.io/Main_Page)打包生成的。
NSIS
NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序。它提供了安装、卸载、系统设置、文件解压缩等功能。这如其名字所指出的那样,NSIS 是通过它的脚本语言来描述安装程序的行为和逻辑的。NSIS 的脚本语言和通常的编程语言有类似的结构和语法,但它是为安装程序这类应用所设计的。
之前使用过NSIS进行过打包,该工具主要通过编写一个脚本文件(*.nsi/*.ini等)完成一系列操作。例子如下 :
用7Z直接解压PCManager_Setup_11.1.6.31(C233D005).exe看看是否可能从修改脚本重打包的方式绕过对设备的检测。
但是在压缩包的根目录并没有找到相关的脚本文件,因此这个方法并不可行。不过既然解压了就试下解压后的可执行文件是否能运行。在解压目录找到PCManager.exe运行。
可以直接运行,难道就此结束吗?跟着窗口的各种提示一路点击"同意",但是还是有问题。
可能是跳过了安装过程没有给安装上服务的原因,点击"修复"。连接手机并进入"多屏协同"。此时并不能正常识别到手机。
到此看来,似乎并不能通过解压的方式正常运行"华为电脑管家"。
分析安装流程中的模块
知道了安装包并不包含主要的逻辑代码,那么就需要从安装包中包含的模块入手分析。
回到开始时用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函数分析完毕。
模块分析总结
通过上面的对其中的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 link
DWORD bytesWritten = 0;
// Query size of SMBIOS data.
// 第一次调用时为了获取SMBIOSData的数据大小
smBiosDataSize = GetSystemFirmwareTable('RSMB', 0, NULL, 0);
// Allocate memory for SMBIOS data
smBiosData = (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);
}
最终效果
声明:以上内容如有侵权部分,敬请告知,将及时更改。
相关引用
BIOS有关更多信息
(https://www.dmtf.org/standards/smbios/)
GitHub DumpSMBIOS
(https://github.com/KunYi/DumpSMBIOS)
MSDN GetSystemFirmwareTable
(https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemfirmwaretable)
华为 matebook
(https://consumer.huawei.com/cn/support/laptops/matebook-e/)