华为 MateBook E 系列笔橡皮擦切换白名单分析与缓解

去年 9 月结束实习的时候,顺手在狗东内销上看上了性价比超高的 MateBook E Go。于是光速拿下。这台机器的骁龙 8cx Gen3 让我十分满意,屏幕和键盘也都很优秀,唯一美中不足是它的笔体验远远不如 Surface。

M-Pencil 唯一的交互只有双击,这一操作可以在华为电脑管家的选件中心自定义操作。这里我选择了和 Apple Pencil 一样唐氏的双击切换橡皮。本以为这个功能会十分好用,但当我在速写白板和 Word 中切换橡皮时,却始终无法正常切换。

查阅华为官网后发现,原来华为唐氏地对双击切换功能设置了白名单…

白名单

白名单和笔驱动原理

既然有白名单,我们首先需要查找白名单的位置。在研究这个之前,我们首先要了解 M-Pencil 在 MateBook E 系列机型上运行的原理。

不知基于什么考虑,华为使用用户态程序来负责设备的驱动逻辑。我们吸附 M-Pencil 和响应双击操作等事件都是由华为电脑管家的选件中心插件处理。它对应的后台进程是 AcAppDaemon.exe。(吐槽华为这帮懒狗,ARM64 的机器这些 OEM 套件全是 x64 的…)

AcAppDaemon

进入它所在的路径 C:\Program Files\Huawei\PCManager\components\accessories_center\accessories_app\AccessoryApp。考虑白名单往往是通过进程名称判断,我们以 onenoteim 搜索,可以发现在 .\Lib\Plugins 下的 AlitaPenApp.dllCD52PenApp.dllCD54PenApp.dllCD54RPenApp.dll 中均对应有这些白名单信息。这些 DLL 对应了不同的笔型号,对于我的二代雪域白 M-Pencil 型号为 CD54,则使用 CD54PenApp.dll

DLL 中的白名单信息

我们首先考虑能不能修改这些 DLL 来避免白名单呢?很遗憾不能,因为 DLL 都经过了华为的数字签名,修改 DLL 会导致数字签名错误而无法加载。

既然不能通过修改 DLL 达成目的,我们可以考虑自行实现一个程序来控制笔的切换。先从日志入手进一步分析,文件 C:\ProgramData\Comms\PCManager\log\AccessoryCenter\AccessoryApp\InfoAcAppDaemon.log 是 AcAppDaemon 的日志输出,触发几次 OneNote 等程序切换以及橡皮擦切换,观察日志内容变化。

日志内容
2024-02-16 22:06:22.8937 P12204 INFO T23 PenMistouchPreventionProc.TransferPenMode MISTAKEN_TOUCH_PREVENTION_MODE---->onenoteim
2024-02-16 22:06:22.8937 P12204 INFO T20 PenMistouchPreventionProc.CallbackTransferPenMode Enter MISTAKEN_TOUCH_PREVENTION_MODE!
2024-02-16 22:06:26.4830 P12204 INFO T23 PenMistouchPreventionProc.TransferPenMode NORMAL_MODE---->WindowsTerminal
2024-02-16 22:06:26.4830 P12204 INFO T20 PenMistouchPreventionProc.CallbackTransferPenMode Enter NORMAL_MODE!
2024-02-16 22:06:34.1585 P12204 INFO T23 PenMistouchPreventionProc.TransferPenMode MISTAKEN_TOUCH_PREVENTION_MODE---->onenoteim
2024-02-16 22:06:34.1614 P12204 INFO T20 PenMistouchPreventionProc.CallbackTransferPenMode Enter MISTAKEN_TOUCH_PREVENTION_MODE!
2024-02-16 22:06:39.7719 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc CallbackPenCurrentFunc, current pen func is 1
2024-02-16 22:06:39.7719 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Enter Show EraserWindowShow.
2024-02-16 22:06:39.7719 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Show EraserWindowShow.
2024-02-16 22:06:47.9331 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc CallbackPenCurrentFunc, current pen func is 0
2024-02-16 22:06:47.9331 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Enter Close EraserWindowShow.
2024-02-16 22:06:47.9331 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Close EraserWindowShow.
2024-02-16 22:06:47.9331 P12204 INFO T20 Window_Eraser.CloseWindow CloseWindow
2024-02-16 22:06:52.2172 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc CallbackPenCurrentFunc, current pen func is 1
2024-02-16 22:06:52.2172 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Enter Show EraserWindowShow.
2024-02-16 22:06:52.2250 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Show EraserWindowShow.
2024-02-16 22:06:54.0669 P12204 INFO T20 

PenMistouchPreventionProc.CallbackPenCurrentFunc CallbackPenCurrentFunc, current pen func is 0
2024-02-16 22:06:54.0669 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Enter Close EraserWindowShow.
2024-02-16 22:06:54.0669 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Close EraserWindowShow.
2024-02-16 22:06:54.0669 P12204 INFO T20 Window_Eraser.CloseWindow CloseWindow
2024-02-16 22:06:56.4327 P12204 INFO T20 PenMistouchPreventionProc.CallbackChargingStatusEvent CallbackUpdatePenChargingStatus, charge status is 1
2024-02-16 22:06:57.2490 P12204 INFO T23 PenMistouchPreventionProc.TransferPenMode NORMAL_MODE---->WindowsTerminal
2024-02-16 22:06:57.2490 P12204 INFO T20 PenMistouchPreventionProc.CallbackTransferPenMode Enter NORMAL_MODE!

从中我们可以发现一些函数名称,例如 CallbackPenCurrentFunc 标识了笔的输入状态。我们基于此进行分析,通过 IDA 可以得知笔的相关服务函数是来自 Depend\PenService.dll

IDA 分析

于是使用 dumpbin 查看导出函数:

Microsoft (R) COFF/PE Dumper Version 14.37.32825.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file Depend\PenService.dll

File Type: DLL

  Section contains the following exports for PenService.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
          60 number of functions
          60 number of names

    ordinal hint RVA      name

          1    0 0000AA50 CommandSendDoubleFuncChr
          2    1 00009D60 CommandSendGetPenBattery
          3    2 00009E00 CommandSendGetPenChargingStatus
          4    3 00009DB0 CommandSendGetPenConnectStatus
          5    4 00009B50 CommandSendGetPenFirmwareVersion
          6    5 00009940 CommandSendGetPenHardwareVersion
          7    6 0000A020 CommandSendGetPenKeyFunc
          8    7 00009F50 CommandSendGetPenKeySupport
          9    8 000094B0 CommandSendGetPenModule
         10    9 00009710 CommandSendGetPenSerialNo
         11    A 0000AA20 CommandSendGlobalPreventionChr
         12    B 00009EA0 CommandSendNewConnectRespond
         13    C 0000A160 CommandSendOskPrevensionMode
         14    D 0000A090 CommandSendPenCloseConnectWindow
         15    E 0000A9B0 CommandSendPenCurrentFunc
         16    F 00009FC0 CommandSendSetPenKeyFunc
         17   10 0000AAA0 CommandSendSysLang
         18   11 0000A9F0 CommandSendTouchChr
         19   12 0000A1E0 CommandSendTpRotateAngle
         20   13 00009500 CommandSendTransferPenMode
         21   14 00002990 FwUpdateStart
         22   15 00002EC0 FwUpdateStop
         23   16 00007DA0 GetInterruptPipeMsg
         24   17 00008980 GetMcuVersion
         25   18 00009BA0 GetPenFirmwareVersion
         26   19 00009990 GetPenHardwareVersion
         27   1A 00009560 GetPenModule
         28   1B 00009760 GetPenSerialNo
         29   1C 0000B620 IsShopDisplayVersion
         30   1D 00007630 IsTabTipKeyboardVisble
         31   1E 0000AE60 ProcPipeMsg
         32   1F 00007710 RegisterCallBack
         33   20 00009F30 RegisterCallBackNewPenConnectRequest
         34   21 0000A050 RegisterCallBackNewPenConnectResult
         35   22 00009540 RegisterCallBackTransferPenMode
         36   23 00009D90 RegisterCallBackUpdateBatteryVolume
         37   24 00009E30 RegisterCallBackUpdatePenChargingStatus
         38   25 00009DE0 RegisterCallBackUpdatePenConnectStatus
         39   26 00009B80 RegisterCallBackUpdatePenFirmwareVersion
         40   27 00009970 RegisterCallBackUpdatePenHardwareVersion
         41   28 0000A000 RegisterCallBackUpdatePenKeyFuncGet
         42   29 00009FA0 RegisterCallBackUpdatePenKeyFuncSet
         43   2A 00009F80 RegisterCallBackUpdatePenKeySupport
         44   2B 000094E0 RegisterCallBackUpdatePenModule
         45   2C 00009740 RegisterCallBackUpdatePenSerialNo
         46   2D 0000A0C0 RegisterCallbackPenCloseConnectWindow
         47   2E 0000AA80 RegisterCallbackPenCurrentFunc
         48   2F 0000A0E0 RegisterCallbackPenDeviationReminder
         49   30 0000A1A0 RegisterCallbackPenFirstBatAfterConn
         50   31 0000A100 RegisterCallbackPenOskPrevensionMode
         51   32 0000A070 RegisterCallbackPenTopBatteryWindow
         52   33 0000A120 RegisterCallbackPenUpdateProgress
         53   34 0000A140 RegisterCallbackPenUpdateRessult
         54   35 0000A990 RegisterCallbackSysLang
         55   36 0000A1C0 RegisterCallbackTpRotateAngle
         56   37 00003580 RegisterLogFunc
         57   38 00008750 RetriveMcuSwVersion
         58   39 000076D0 StopLoop
         59   3A 0000B000 StopProcPipeMsg
         60   3B 00003590 UnRegisterLogFunc

  Summary

        A000 .data
        1000 .pdata
        6000 .rdata
        1000 .reloc
        1000 .rsrc
        D000 .text

我们观察到先前 CD54PenApp.dll 是通过 RegisterCallbackPenCurrentFunc 得到的笔输入功能状态变更回调。那么相应的,CommandSendPenCurrentFunc 应当就是笔输入状态的修改。

测试后果然如此,CommandSendPenCurrentFunc(1) 代表切换进入橡皮擦模式,CommandSendPenCurrentFunc(0) 代表切换进入笔模式。

于是接下来的难点就在于得到双击事件了。

双击事件获取

之前我在 GitHub 上发现一个 gist 文档 dantmnf/Huawei MateBook E Go (Wi-Fi).md 中,提到华为旧版的笔控制程序 HuaweiPenApp,在安装它之后我发现它比起新版选件中心多了一个“使用 Windows Ink 双击按键设置”。

旧版笔控制程序

而我在研究时发现有一个类似的笔增强项目 eiyooooo/MateBook-E-Pen,它的第三方 app 橡皮切换是通过获取笔和橡皮切换控件实现的,这不优雅。但是我发现它能得到点击事件,这让我好奇它是如何实现的。

同时,我注意到使用 MateBook-E-Pen.exe 后,旧版笔控制程序将设置自动切换为了“使用 Windows Ink 双击按键设置”。这不禁让我怀疑其中有着某些联系。虽然这个项目的代码实在有些混乱,但是至少这一功能应当是和 PenService 有关。观察几个参数后,我作出了以下猜测:

  • 由于旧版笔控制程序按键设置,“截屏”、“打开智慧语音功能”、“使用 Windows Ink 双击按键设置”和“关闭”按 0,1,2,3 顺序排列,“使用 Windows Ink 双击按键设置”的下标或许为 2。
  • 这个项目唯一我不确定功能的导出函数调用是 CommandSendSetPenKeyFunc(2);,或许它就是切换设置的根源。

经过测试,我的猜测是正确的,那么只需要知道如何监听 Windows Ink 的双击按键事件即可。

查阅微软官方文档《Windows 笔设计》后,我得知双击操作在 Windows 中是绑定为 Win+F19 的特殊虚拟组合键的。

虚拟快捷键

那么现在一切思路就清晰了。

自制切换程序实现

为了方便,我使用 Python 编写了一个自制的切换程序,它具备以下功能:

  • 加载 PenService DLL,能够控制笔的输入模式以及双击按键设置
  • 监听 Win+F19 快捷键组合,切换笔的输入模式
  • 提供一个图标显示笔的当前输入模式的状态

然后我使用 PyInstaller 打了一个包,在使用它之前,你需要先结束掉 AcAppDaemon.exe,然后去 C:\Program C:\Program Files\Huawei\PCManager\components\accessories_center\accessories_app\AccessoryApp\Lib\Plugins 下面删除和你的笔相关的 DLL 来阻止原先的笔事件响应程序加载。

程序截图

代码我已经开源到 GitHub,可执行文件也可以直接从我的服务器下载

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注