Z2A-BlackLotus Challenge stage 2 bootkit-rootkit analysis
BlackLotus 阶段 2 bootkit-rootkit 分析
在我们深入探讨这个神圣的玩意儿之前(相信我,这确实是某种神圣的玩意儿,因为没有人能在没有上帝旨意的情况下做到这件事(至少我是这么认为的)),这里是 bootkit 文件的哈希值
首先,这是一个健康系统的样子
identifier {bootmgr} device partition=\Device\HarddiskVolume9 path \EFI\MICROSOFT\BOOT\BOOTMGFW.EFI description Windows Boot Manager locale en-US inherit {globalsettings} default {current} resumeobject {3f80ecd0-df10-11ed-bafc-80a84b2564bb} displayorder {current} toolsdisplayorder {memdiag} timeout 30
identifier {current} device partition=C: path \Windows\system32\winload.efi description Windows 10 locale en-US inherit {bootloadersettings} recoverysequence {3f80ecd2-df10-11ed-bafc-80a84b2564bb} displaymessageoverride Recovery recoveryenabled Yes isolatedcontext Yes allowedinmemorysettings 0x15000075 osdevice partition=C: systemroot \Windows resumeobject {3f80ecd0-df10-11ed-bafc-80a84b2564bb} nx OptIn bootmenupolicy Standard
现在说到我的分析,我从未成功感染过我的机器,因此我将使用前面提到的亚洲研究员的博客文章中的示例,这就是感染后应有的样子。```
// Windows Boot Manager
// --------------------
// identifier {9dea862c-5cdd-4e70-acc1-f32b344d4795}
// description Windows Boot Manager
// locale en-US
// inherit {7ea2e1ac-2e61-4728-aaa3-896d9d0a9f0e}
// bootdebug Yes
// displayorder {57e1b615-0355-11ec-abb0-005056c00008}
// timeout 30
// Windows Boot Loader
// -------------------
// identifier {57e1b615-0355-11ec-abb0-005056c00008}
// device boot
// path \system32\hvloader.efi
// description Hoy la disco se flota
// locale en-US
// inherit {6efb52bf-1766-41db-a6b3-0ee5eff72bd7}
// truncatememory 0x10000000
// avoidlowmemory 0x1000
// nointegritychecks Yes
// testsigning Yes
// isolatedcontext Yes
// osdevice boot
// systemroot \
// ems Yes
=============================================================================
=============================================================================
在我们开始之前,到底如何为分析 EFI 模块搭建环境呢?这要感谢 @MaverickMusic__,在一次与他的讨论中,他给了我这样一个链接( https://zhuanlan-zhihu-com.translate.goog/p/343293521?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en-GB )。我并没有完全照搬那里的步骤,下面是我为了把环境跑起来所实际做的:
-首先,我安装了 edk2(https://github.com/tianocore/tianocore.github.io/wiki/Windows-systems)
-其次,我将 ovmf 配置为 debug 构建而不是 release 构建(这对我们后面有帮助)。我用到的命令是 build -a X64 -t VS2019 -b DEBUG -p OvmfPkg/OvmfPkgX64.dsc
-第三,我不得不配置我的 windbg。我到底是怎么做到的呢?我从这个链接下载了所有东西(git clone https://github.com/microsoft/WinDbg-Samples)。然后我编译了 ExdiGdbSrv.sln。接着我完全按照这个链接(https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-qemu-kernel-mode-debugging-using-exdi)上的内容,从它所说的 Use regsvr32 to register the DLL in an Administrator command prompt. 一直到 PS>.\Start-ExdiDebugger.ps1 -ExdiTarget "QEMU" -GdbPort 1234 -Architecture x64 -ExdiDropPath "C:\path\to\built\exdi\files"。我知道这很让人困惑,但请耐心等待,我一定会制作一个视频,逐步解释每一步!
好了,既然我们已经搭好了调试环境,那到底该如何调试代码呢?所以我们要启动 qemu,就我而言,我是通过执行 qemu-system-x86_64.exe -L . -bios OVMF.fd -hdd dos.img -debugcon file:debug.log -global isa-debugcon.iobase=0x402 来启动的。运行 qemu 命令后,我立即进入 qemu 的视图菜单并选择了 compat_monitor0。完成这些操作后,你应该会看到类似下面的样子。
另外,在选择这个之后,你应该输入 gdbserver 来启动一个 gdb 远程调试实例,之后我们将用 windbg 通过以下命令附加到该实例:.\Start-ExdiDebugger.ps1 -ExdiTarget "QEMU" -GdbPort 1234 -Architecture x64。好了,一旦我们连接上去,就会看到下面这样:
太酷了,现在来理解这个输出。就我们的情况而言,唯一相关的行是 EntryPoint=0x000062C9A8C,它类似于我们运行 bootkit 时首选的加载地址。具体来说,对于该 bootkit,它在 0x62C4A8C 或 0x62C9A8C 之间变化。现在我们可以在 ida 中重新基址程序,然后进行正常工作 :) 。尽情享受博客的其余内容吧!
=============================================================================
将原始 winload.efi 与 blacklotus 释放的那个进行 BinDiff 比较
我们看到一些相似之处,但也有一些差异,不过没什么有用的,反正……
=============================================================================
酷,那我们开始吧。
酷,让我们开始剖析。首先我们看到了一个被调用的函数。酷,那它是什么呢?嗯
酷,又一个函数。不太像……有没有注意到什么熟悉的东西?

还没有东西???
没问题,也许现在
同一个 demangle 函数!嗨,老朋友 :)))
酷,但 return (*(a1 + 88))(v2, &unk_62CEABC, 3i64); 呢??说实话,仅从静态角度我确实不知道该怎么说,所以让我们尝试用调试器来理解它 :))
所以当我们对字符串进行 demangle 时,我们得到
接下来我们到达调用指令时
我们没有任何信息……太棒了,但为什么会这样?因为我们没有 .pdb 文件,所以无法获取调试符号……酷,至少 ida 在这里很有帮助。所以我们知道这个 “grand” 函数接收 SystemTable->RuntimeServices 作为输入,它的类型是 EFI_SYSTEM_TABLE。酷,如果我们检查它,这是一个 A pointer to the EFI Runtime Services Table. 。如果我们用谷歌搜索,我们会找到一堆文档,但其中一个关键的文档是 https://uefi.org/sites/default/files/resources/UEFI\_Spec\_2\_1\_D.pdf 。上面写着
酷,所以这是一个带有许多指针的结构体,是的,但让我们再放大一点。
所以首先它 demangle 了 VbsPolicyDisable,如果我们用谷歌搜索,会看到 eset 的分析报告,其中指出 that this variable is evaluated by the Windows OS loader during boot and if defined, the core VBS features, such as HVCI and Credential Guard will not be initialized. ,所以基本上这个变量负责启动级别的 “安全” 状态。酷,接下来我们有一个函数接收该变量并
因此我们可以得出结论,这一定是一个以某种方式改变该变量状态的函数。酷,那么有哪些可能的函数能做到这一点呢?在 EFI_SYSTEM_TABLE 中只有一个这样的函数,即 EFI_SET_VARIABLE SetVariable;
所以我们得出结论,这个函数只是简单地获取 VbsPolicyDisable 并将其设置为``` db 77h ; w .data:0000000180005034 db 59h ; Y .data:0000000180005035 db 3 .data:0000000180005036 db 32h ; 2 .data:0000000180005037 db 4Dh ; M .data:0000000180005038 db 0BDh ; ½ .data:0000000180005039 db 60h ; ` .data:000000018000503A db 28h ; ( .data:000000018000503B db 0F4h ; ô .data:000000018000503C db 0E7h ; ç .data:000000018000503D db 8Fh .data:000000018000503E db 78h ; x .data:000000018000503F db 4Bh ; K.
那么这些字节有什么重要的吗?嗯,是的,如果你碰巧读过 BlackLotus 分析的第一部分,你会知道我引用了一位亚洲研究员的工作。那位研究员很慷慨地也分析了被丢弃的 bootkit。请看看这个(https://www.cnblogs.com/DirWang/p/17294545.html#autoid-3-2-1),在他的分析中,他很好心地给了我们这些信息。他指引我们看 https://github.com/Mattiwatti/EfiGuard/blob/master/EfiGuardDxe/PatchWinload.c 。在那里我们看到了类似的一行
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/33c755aa460f5bdc338c75f3f5e1322c731123299d59c43dcdf2be3b72d1276c.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/144b057568871a60a32e477ed1ae7e8d7090778a67b7fc24222f0f58fe44afc1.png" alt=""><figcaption></figcaption></figure>
</div>
这背后是否有特定的原因?老实说我不知道,这是我第一次分析 bootkit。如果你在这方面比我更有经验,请告诉我,或者提交一个 PR/Pull Request 来编辑本文档 :)
\=============================================================================
酷,接下来,运气站在我们这边,IDA 的伪代码和汇编代码很相似。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/7457c9d8beb807556618728c7aba102109a472ef2f990f35ec344038fafc4cd5.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/e11c4b4e4260f7a91e6dc08bcabb7c3ac420dcee346b1bf9dff02835342f7837.png" alt=""><figcaption></figcaption></figure>
</div>
所以我猜这里发生的是 EFI\_SYSTEM\_TABLE 的正常初始化,我猜它基本上初始化了要继续引导进程的进程。然后我们调用了 PatchBootManager 函数。
\=============================================================================
PatchBootManager
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/66c49bf5b64655af7e0edfe384ad715912c02fbeea1ac1313e4f65479ab58f80.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/7a1dc3ac7c01cbd4cfd9b3ec614c357895210e4f03ae5d58000e6b047f84eb96.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/3fc22a51052c24c9e436ace83a1672ce88bf25fb11103b32d0143fa0ab4d7216.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/22c7869b067f26c6469e4baffcd4226535c47e82d4baaf7fa8e27820915b1580.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/38cf7697523141faa68a59b52abd2a1ca14d0d9053f1e6960052a5093e3a94bb.png" alt="3">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/505a3fd02b96bcb823bf108ee03a8a1204827852260c1e8ce3902638237259b3.png" alt=""><figcaption></figcaption></figure>
</div>
从伪代码来看
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/9c8482f0faa4d4de5f8643acc4276ebbbfc57386e9b00a4057793b5c67b1bf3b.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/c44de61b9f089fd8972bcc8567110bc2d58c21e5465a7b4f14fc7beaefee30a3.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/6809c28e881b3626d73a476cd31631891539f6a7b71cf276619f9cd2244d83e9.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/c8cd4870cd0488e57cde3448ba01fdd44063f266516d8530731f42e6ccc5be78.png" alt=""><figcaption></figcaption></figure>
</div>
好吧,所以我们看到的第一个函数调用是 HandleProtocol。那这段代码是做什么的呢?幸运的是,我们快速用 Google 搜索时遇到了这个 (https://tianocore-docs.github.io/edk2-ModuleWriteGuide/draft/5\_uefi\_drivers/54\_communication\_between\_uefi\_drivers.html),我们看到它 `retrieve protocols`。酷,实际上我并不能理解太多。兄弟,我懂你。所以基本上这是用来检索其他 UEFI 驱动使用的通信信息方法。酷,再挖一点。我们看到第二个参数是
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/703c3f52bd8c3d10eaf046fc801cbc60ca7a62e9162d67a6ba63caf4c9e9038d.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/9d41aacaed9c15391beb8b1b62739572bf7f873508938ade770610eec371a17c.png" alt=""><figcaption></figcaption></figure>
</div>
如果我们搜索那些特定的字节,我们会遇到这个
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/f4a8ed3b81486c8481c0f8894175f9d7390ddf62f9974ba025aad9827dea119e.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/882b5116cfd08886c3dac510443fb77c9aa31781eb234208ec81fdf313544c29.png" alt=""><figcaption></figcaption></figure>
</div>
那么 EFI\_LOADED\_IMAGE\_PROTOCOL\_GUID 到底有什么用呢?引用自(https://uefi.org/specs/UEFI/2.10/09\_Protocols\_EFI\_Loaded\_Image.html) `可用于任何镜像句柄,以获取有关已加载镜像的信息。` ,哪种类型的信息? \`\`\`本部分定义了 EFI\_LOADED\_IMAGE\_PROTOCOL 和 EFI\_LOADED\_IMAGE\_DEVICE\_PATH\_PROTOCOL。分别而言,这些协议描述了一个已加载到内存中的 Image,并指定了通过 EFI Boot Service LoadImage() 加载 PE/COFF 镜像时使用的设备路径。这些描述包括镜像加载的来源、镜像在内存中的当前位置、为镜像分配的内存类型,以及镜像被调用时传递给它的参数。\`\`\`\`
所以在我们的例子中,它获取的是关于 bootkit 的信息。现在有个问题:我们无法真正检查函数的结果,因为我们没有调试符号 :/ 但我们可以推测。而我倾向于推测该结构(前一个函数调用的结果)会放在 rbx 中。
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/b8f744ecd9fd4be572a1e2d191b06231559eca2b32aeaa981a399eec7f3ee710.png" alt=""><figcaption></figcaption></figure>
接下来我们调用 demangle string
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/1fdc75d2dc9221250e6d46692770ad5240f7a0d6cda9cc5634749b327fc8c146.png" alt=""><figcaption></figcaption></figure>
这让我们得到
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/e073b265ff13675b755ec8413554bec74ab4eae57dd002b25694632eb43cbc8e.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/3654bb54071386cd020c43ed4a35fae40aec386897a0ca26a27b58b8e1a91c95.png" alt=""><figcaption></figcaption></figure>
</div>
然后我们调用
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/c433aa63cddd2ecfb5ce0fd57a6774ad35b1c1ed4ee71e7cecb566cc861d2b80.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/6834be566cdd81f2410ec7d4ee97936d14a27017a723371eea3046b8590609f4.png" alt=""><figcaption></figcaption></figure>
</div>
\=============================================================================
sub\_180002B14
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/483f2b789c0fa6fe9378ba324f8e349bc72cbb45e540bbc2355ef55acaf1b6b4.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/d84d79115facbc0f9ff7afe06d70a18555e7d94bc196f6f9d0ff6b5cfd40408a.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/19e2366fddab99fa3aa4375b8306475ee31d3bf9a3c3737efeeb4d627868404e.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/6c808c76821c756eca776cf6290482f6f9829167f539bf31b2ad82b3de0af829.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/ad0d06238e39e4ba1decb2f1305510a8375822b13ae9148d91e267d14762eb3b.png" alt="3">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/0e45e9ae64c3fbd8a61ffdf60ec2e532529d8cab6cfc7786fa2c91824233d347.png" alt=""><figcaption></figcaption></figure>
</div>
以及伪代码
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/c14b1eacbe8eef4155c4148b66fa95f93bbd9e6fc81e5ca80f1ddc5b2b9a7a16.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/b7c452865661d95b79886a48a455eba663c2e3915e0a81ad16dc7ddd8c2c0189.png" alt=""><figcaption></figcaption></figure>
</div>
酷,所以在 if 之前一切都一目了然,那么这个 if 呢?我们看到它又一次以 unk\_180005010 作为参数进行调用,这又是一个字节数组,进一步检查后,它看起来是这样的
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/c3bb66f35a4e559ae793ba590fc0c342bdde1e1c9765e144cb450a809009655d.png" alt=""><figcaption></figcaption></figure>
现在,如果我们再次检查前几个字节并快速搜索,我们会找到这个 (https://github.com/theopolis/uefi-firmware-parser/blob/master/uefi\_firmware/guids/efiguids\_ami.py),更确切地说是这个 `'EFI_DEVICE_PATH_PROTOCOL_GUID': [0x09576e91, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b]`。
如果我们再次查看 UEFI 规范页面,我们会看到 `可用于任何设备句柄,以获取有关物理设备或逻辑设备的通用路径/位置信息`。酷,我们还看到更多类似的内容 `设备路径描述了句柄所指设备的位置`。好吧,酷,如果我们稍微往下滚动,我们会看到一个名为 \_EFI\_DEVICE\_PATH\_PROTOCOL 的函数。好了,总结一下,我们知道这与 EFI\_DEVICE\_PATH\_PROTOCOL\_GUID 有关,但我们的函数类型是 EFI\_BOOT\_SERVICES。那么在 EFI\_BOOT\_SERVICES 中是否有任何函数可以做类似处理协议的事情呢?有的。如果我们查看 https://www.intel.com/content/dam/doc/product-specification/efi-v1-10-specification.pdf 的第 4.4 节,我们会看到
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/918a0a5184849cbb87454df44c5ffa5e5fa586f223213287f6c7fbe737a0fbc8.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/fab94604327fe596d9eeb68368efcf0afc721ef7bfcdc07a8977392b4309238c.png" alt=""><figcaption></figcaption></figure>
</div>
更确切地说,它有一个我们熟悉的函数(HandleProtocol)。酷
接下来我们看到另一个函数调用,这次我们不认识它。让我们看看它接受什么参数:它接受 2 个,然后是传入字符串的长度(以 unicode 表示),以及一个指向变量的指针。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/39e0d1d9baabff8d95da7109294db94f9fc4409a6ede34d37ece9662fb73651d.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/e116e90f20a09d48f21a981e7d24c49f7474ea9ac6007b6fa41973300af4e83d.png" alt=""><figcaption></figcaption></figure>
</div>
现在,如果我们在调试器中检查这个
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/f412c3bf83f60da9213f4c29b39350c925a0aa6411c42de52d7fa4530c5bc3fe.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/5285bf4d5e52dbbef857e46e8738c3ba6e7f6cda87f9b67a4965b23cc70aba0e.png" alt=""><figcaption></figcaption></figure>
</div>
我们看到一个奇怪的东西:rcx 有一个调试字符串 AllocatePool,它出现在一个函数调用之后,因此我们推断这可能是一个对 AllocatePool 的调用。有趣的是,如果你也查看规范,你会发现 boot\_services 中也有一个指向 AllocatePool 的指针,这只会让我们的假设更加强烈。
酷,所以如果我们成功分配了足够的空间(检查是否 >= 0 是为了检查我们是否成功分配,因为如果 EFI\_OUT\_OF\_RESOURCES 被实现为
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/bf08cd5dd33ad9ca16ff4e6a44a4cbfd51cd1f8b0300d9b6b44984c4306fcb6e.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/c28af9995294473937be13a6094de90cb57a688c83727617ca9f667c2705d7cd.png" alt=""><figcaption></figcaption></figure>
</div>
那么我们只能安全地假设
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/3741ef8be2dc989f601d26568907381f1847d29c0bf514add76485b0f0fef4b6.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/cdfc6ccb296659fc8156d4bfba6d777da8b2792e4af56677d0f0d57695932216.png" alt=""><figcaption></figcaption></figure>
</div>
是用于成功分配的)
一个有趣的事实是,分配后的缓冲区并不是零,而是包含这些字节。如果有人对此了解更多,请提交一个 PR 请求来编辑本文档。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/e4b15da615e07faa4965d864435bf4deec0e01270f9d1ac1134fb5b20ad46892.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/b229a700b321040fc8978f4f0f489a058381795c128d27dbcfbcfff5f9ffd3b2.png" alt=""><figcaption></figcaption></figure>
</div>
所以,总之,我们最终调用了 memcpy,调用后我们的缓冲区看起来像这样
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/28e61d8725687245e9a9dae7e467929ac15d5a81e33178262f0ce886a3c4555c.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/f161ab0441b14d71dbade9fc3acebbd12b1453de5d53b77b09af98c0cf0ac6d6.png" alt=""><figcaption></figcaption></figure>
</div>
然后我们追加一些字节,使缓冲区看起来像这样
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/80906384a49ef1d6eaea9667cb962459c27feb8e9deac857491a248be5443a4f.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/7c703952c72c154d156603c457a00013305c48519ddee808925c32a62499135f.png" alt=""><figcaption></figcaption></figure>
</div>
然后我们调用一个名为 FileDevicePath\_call 的函数,它大概长这样 
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/66c77ae12715ee6d982ba2cfb769ba6a650f52ce56d7bae54e3bd93686695f02.png" alt=""><figcaption></figcaption></figure>
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/5f7e06800ec3eecb9559d1592b8a76868038c0cbb64744e5c928b65d189ef183.png" alt=""><figcaption></figcaption></figure>
并转换成了这样
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/2b36492bb7dd46a7139c27de61371b59adac5bfbec989bbb542b58d34ce7982d.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/361e2b338c676b99393f7baa48e970ba886894f579065a4468fe113841c0783b.png" alt=""><figcaption></figcaption></figure>
</div>
酷,但如果不解释的话,这毫无意义,所以....
首先,我们有一个自定义的 strlen 实现,我们不会去剖析它,因为它没什么用 :) 但这是结果
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/03a0aabe9f7c87f42b7e64bb284d74738ecdc81134136cfec68c70084644c3ea.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/823c30bd0a6a8c1d750ef1dfb0bce5be67a13a96de12e53b3e46b2226a1c2be1.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/5dd4684c37f3129c8234cd69574bb6371b4bef72ff7e027199b7900134cdd101.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/118ee749874a6b08d40286821735190f67af45ca8d4573fc94aee3a2cea04af2.png" alt=""><figcaption></figcaption></figure>
</div>
len(of(str)+"\x00") 产生 32 个字符,然后在函数调用前追加最后 4 个字节 0x4FF7F。
接下来我们调用了我在那位亚洲研究员博客文章中也用到的 PxepDevicePathInstanceCount,它其实就是 strlen,因为它只是计算每个字符并有一个计数器。如下所示
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/130931a6676f0519da2b50a9762955b7581e7102434e587236137b216cc62ead.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/ce1a6ad93452a8092d2fb71ffd6418df78c32fc8cdf4fb99133924cc9037f12e.png" alt=""><figcaption></figcaption></figure>
</div>
所以是的,我们看到 pop rbx,调用后我们看到 rbx=0x48
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/4c2eabdd91ec40fc82c100e67579d729def9e74c6bfd000fff287bc9c10b0f38.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/818bb3c8398d4a53c0260d9310d4d192cfa5b65a404348df5516c94fa3d8dcaa.png" alt=""><figcaption></figcaption></figure>
</div>
然后我们再次对同一个字符串调用 strlen,我猜这是因为下一行,准确地说,`v6 + v4 * v5;` 中我们做了 v4\*v5,这大概是一种处理 unicode 字符串的方式,我猜。
不管怎样,然后我们再次使用 gEfiBootServices + 64 分配内存,之前我们遇到过它,它被解析为 AllocatePool。
所以这里我们还看到一些有趣的东西,那就是
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/26d19e06bd335f1267946a5da2542cf3eb9f2965fab788d0bcf720d7e3c08d37.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/225c92cbe9a5e72214cb279e61ad5fcbaef0a243db835b67fce5ff7ef74e7c86.png" alt=""><figcaption></figcaption></figure>
</div>
这里的内存块中具有 afafafaf 模式。
接下来发生的是,在主循环执行后,我们得到两个缓冲区,它们看起来像这样
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/35f257d96c752ac436e81778da5f37c28bfcb79fc5614f3ea8e099888cc011c3.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/b9c706cc28161a6e7274c5cabc895bedc1279728b3ed7c11084cb6cb38dbf9e3.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/52477a3825650e47112a5e619a098c74e7e0de778ebfeda5682e5b49d48f3923.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/762a6dd3897f3b630ce1d0faa371e58ed9b2bce755bb8edd54f958aab7c3b69b.png" alt=""><figcaption></figcaption></figure>
</div>
老实说,我们只对第一个感兴趣,因为返回的就是它,所以我们可以推测这大概只是复制设备路径并清除缓冲区中的一些垃圾数据。:))
在我们处理完这些之后,我们会检查我们这种情况下的设备路径是否已经(我猜)初始化,如果没有,就释放内存池,然后返回前面提到的函数中那个更干净的缓冲区。
在我们结束这个函数之前,我想指出另一个有趣的事实,这就是 bootservice 表在内存中的样子 :) 根据规范,它看起来带有起始头部。我只是觉得把它放在这里可能对任何想做进一步工作并发现自己在 dump 中找到 BOOTSERVF 字符串的人有帮助,这绝对是 bootservice 表。
\=============================================================================
好了,接下来会发生什么?嗯,我们检查是否成功定位到 winload.efi 文件,并将其加载到内存中。这是伪代码 :)
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/b59f211cb997e5ad783cbb6421833c23bea4ef4f7f21743577cd8aba4656ad2f.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/a96f7410f6bd6f7e207b76dff36a411f3c17a3f40dcb1a9433d5292b46f775e2.png" alt=""><figcaption></figcaption></figure>
</div>
它在内存中的样子是这样的
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/e3ee7b08fdc84433a08e60c7065c74692fb3a4e26eb81def063b662aa5229558.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/445d40332ba97516eebf129d17c56a38e28688858515d787ebf90a99580daa51.png" alt=""><figcaption></figcaption></figure>
</div>
rax 是什么?rax 是镜像的句柄 :) 别像我一样,一开始还以为它是一个内存区域,别犯傻 :)
酷,在继续深入之前,让我快速解释一下 winload.efi 到底是什么。所以,`随着计算机的发展,传统的 BIOS 引导已经过时,关于 UEFI 引导的安全对抗已经开始。从下面的流程图可以看出,MBR 和 VBR 在 UEFI 中不再存在,而是由 UEFI 本身负责加载 bootmgr,这也意味着更安全、更快`
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/d2443977ce3d56790e424fd6236d1d0594db283caa9357ed4e6abb9b8b30b8ed.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/1259e1f45b2d2bb8edcb12609e057d089f7c1dcf907677973d799049c58bee4a.png" alt=""><figcaption></figcaption></figure>
</div>
那么一台普通的 Windows PC 是如何引导的呢?在 BDS 之后,存储在 SPI 中的 UEFI 固件代码完成了工作,然后 UEFI 固件引导管理器首先查询 NVRAM UEFI 变量以找到 ESP,并找到特定于操作系统的引导管理器 bootmgfw.efi,调用其入口函数(DXE 驱动)。
这个函数将首先调用 EfiInitCreateInputParametersEx 函数,该函数主要用于将 EfiEntry 参数转换为 bootmgfw.efi 期望的参数格式。
然后调用 Windows Boot Manager 入口点 BmMain 函数。
在这个函数中,会调用 BmFwInitializeBootDirectoryPath 来初始化启动应用程序(BootDirectory)路径(\EFI\Microsoft\Boot)。
然后 BootMgr 将读取系统引导配置字母(BCD),如果有多个引导选项,它会调用 BmDisplayGetBootMenuStatus 来显示引导菜单。
然后它会调用 BmpLaunchBootEntry 函数来启动应用程序(winload.efi)。
当然,bootmgfw.efi 的作用不止这些,还包括引导策略验证代码完整性以及初始化安全引导组件,所以我不再赘述。
在 Windows Boot Manager (BootMgr) 的最后阶段,BmpLaunchBootEntry 函数将根据之前的 BCD 值选择正确的引导项。如果启用了全卷加密(BitLocker),系统分区将首先被解密,然后控制权可以移交给 winload.efi。
接下来,调用 BmTransferExecution 函数,检查启动选项,并将执行流传递给 BlImgStartBootApplication 函数。
然后 BlImgStartBootApplication 函数将调用 ImgFwStartBootApplication 函数,最后调用 ImgArchStartBootApplication 函数。在其中,将初始化 winload.efi 的内存保护模式,然后调用 BlpArchTransferTo64BitApplication 函数,BlpArchTransferTo64BitApplication 调用 Archpx64TransferTo64BitApplicationAsm 函数,最终将控制权移交给 winload.efi。
这个函数将启用新的 GDT 和 IDT,然后将控制权完全移交给 winload.efi。此时,BootMgr 完成其使命,Winload 开始工作。——以上引文摘自(偷自)一个讨论此话题的中文网站(更多内容请查看 https://bbs.kanxue.com/thread-268267.htm )而从那里,winload.efi 执行它的工作,即加载 windows 并在将控制权交给内核之前做一些更多的硬件工作。
现在,在我们刚才所说的这个简短介绍之后
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/65537ae888c278e8e487029f93d909fb1e4699069699cf1b7d5ad8fa1f9e0d69.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/3b00c68a215f4568fca5d672ea6f892fecf9809e671c68795c067d2660fa3f1f.png" alt=""><figcaption></figcaption></figure>
</div>
我们进一步检查它是否成功加载到内存中,然后我们调用一个名为 ati\_analysis\_rdtsc\_aia\_cu\_4e1f 的函数,如果你已经读过本分析的第一部分,你应该对它有所了解。
现在,为了有趣,让我们假装我们未能分析那个函数,结果被检测到了。让我们看看 sub\_180002A08 长什么样。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/4eb3ae379ab937e19e6a699339d992b2a5fd1fcb33202ca67355f62a5b1203b4.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/a4b252df16a31b5bf165d2a500ebe3c54ad5dad3c58cf9fe88a8a1da11ed6b63.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/dd594955a663274e25603804c348b516ca22755886b2732ef81dc8e1e0177544.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/6fba01328584e8abb254912b81b048e8dcf80bda6d3eadc11573a52a5e84dd15.png" alt=""><figcaption></figcaption></figure>
</div>
我们再次看到 gEfiSystemTable + 64,但这次我们实际上并不知道它是什么,因为它的类型不同,这次它并不是 bootservices 类型,而是 efisystemtable 类型。然后是 memcpy 和另外 3 个我们现在还不知道的函数调用,如果我们运行到循环开始之前
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/20e0db7308860023b613310ea14e0880529a588540d9dabac2218b52dc93cefb.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/b8f498165a4016c19d8d5797517680fc32dea241d343b38fcc613620270d58dd.png" alt=""><figcaption></figcaption></figure>
</div>
如果我们检查 memcpy 之前的参数
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/21f546fbfb10a39e0e603186c8c505a3ae0679e8c7bad40f31643022f9ebfbcd.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/2ef8c97938b8f961abf92c03937ca15d5a48cd03eb58a0169891559fad27dde5.png" alt=""><figcaption></figcaption></figure>
</div>
然后我们检查 QEMU 的输出映像,就会看到
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/e4325617bfda31fb45f7920d0d1d977103fd6b85260e590fc7cfdca1efacb787.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/04e29142b4cf543a6e5b8f55de2c27d1f3f0034707a6d19e2b1ea45b8251e899.png" alt=""><figcaption></figcaption></figure>
</div>
好的,让我们来理解一下这个,我会再次参考 Asian 的研究博客文章,因为老实说,我在这里已经迷失了。
所以在他的博客中,他说这两个函数实际上是```
ConOut->ClearScreen(ConOut);
ConOut->OutputString(ConOut, String);
好吧,但 conOut 到底是什么鬼?他还说 conout 的类型是 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL,而且 conout 是通过 ConOut = gEfiSystemTable->ConOut; 取得的。好吧,那么在代码里这到底是什么意思??
好吧,那我们深入看看吧
定义
以及 guid
现在我这个自作聪明的家伙忘了在调试器里实际捕获这个,因为最初分析的时候我把 efisystemtable 和 bootservices 之间的数据类型搞混了,还以为这实际上是 allocatepool。
那么这些函数是做什么的呢?
嗯,ClearScreen 应该不言自明,OutputString 也一样。研究员是怎么得出那个变量是 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 类型的结论的呢?嗯,大概他是在调试器里看到了 guid 字节吧。
那最后一个函数呢?
嗯,在他的博客文章里他说最后一个函数是 gEfiBootServices->Stall?那么这他妈是干什么的?根据 UEFI 规范 The Stall() function stalls execution on the processor for at least the requested number of microseconds. Execution of the processor is not yielded for the duration of the stall.
所以基本上它会让我们的 CPU 冻结。酷,冻结多久?0x1C9C380 秒。要我说,这时间可真他妈长。而且这又被放进了一个无限循环里,所以是的,我们完蛋了 :)))
这是在调试器中的样子
现在继续我们的主函数
如果我们成功加载了 bootmgfrw.efi(因为这里 winload.efi 才是真正的 Windows 引导加载程序),我们就会调用 sub_180002538
============================================================================= sub_180002538
从图形(graph)视角来看
从汇编视角来看
是不是觉得有点眼熟了?还没有?好吧,给它一点时间,你会想起来的。与此同时,来看看伪代码视角
我们看到对某个 exe 的解析 :) 我不知道它和上一部分(part1)里的有多相似,但让我们看看 :)
所以我们将二进制文件(bootmgfrw.efi)的内存版本与经典的 MZ 头(0x5A4D)进行比较,正如你所见
好的,接下来我们做另一个经典检查,即是否能找到 PE 头
酷,接下来我们调用 sub_1800024C4(),它看起来像这样
酷,这里发生的事情是我们在内存中定位某些值,如果找到了就返回它们。模拟请参考 sub_180002538.py.py。
总之,这是 sub_180002464
如果我们成功执行了 sub_1800024C4,就会返回到更大的函数中,并继续执行一些检查。太棒了,让我们来理清这些。
酷,所以我们进一步比较 rax+0xe 处的值与 0x64。嗯,有趣,检查一下 rax+0xe
这个特定检查背后有什么特别的原因吗?老实说我不知道。如果你知道,请发起一个 pull request 并编辑本文档。
我们再做一些加法,然后进行比较
我想在此停一下,再次引用这篇博文之前的灵感来源,每当我迷失方向时都会参考它。在他的博客中,他把那个比较值的函数重命名为 RtlpImageDirectoryEntryToDataEx,我们搜索了一下,没有结果,但有个和他的命名足够接近的东西,那就是 RtlImageDirectoryEntryToData ,它基本上做的是这件事 Given the base address of a kernel module and the index of an entry in the data directory, RtlImageDirectoryEntryToData() returns the virtual address and the size of the directory entry(https://codemachine.com/articles/top\_ten\_kernel\_apis.html)。在我们的场景中,由于我们处于一个 efi/uefi 应用中,我们可以认为我们看到的 50 是大小,单位是字节还是 MB 我在这里不确定,反正是我们根分区的大小,而 rax 中的那个地址则是我们目录中的一个条目。
在我们进一步继续之前,还有一个有趣的细节需要解释。在他的研究中,他把 RtlImageDirectoryEntryToData 的输出转换成了这个结构``` typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { union { struct { DWORD NameOffset : 31; DWORD NameIsString : 1; }; DWORD Name; WORD Id; }; union { DWORD OffsetToData; struct { DWORD OffsetToDirectory : 31; DWORD DataIsDirectory : 1; }; }; } IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
现在,这个结构到底是什么鬼?
嗯,快速搜索一下这个结构,我们会来到这里(http://www.brokenthorn.com/Resources/OSDevPE.html),它告诉我们:`解析资源比其他目录类型要复杂一些,不过。和其他节一样,可以从可选头部的 DataDirectory 成员中获取一个基础的 IMAGE_RESOURCE_DIRECTORY 结构:等等等等`,还有 \`\`\`这个结构除了最后三个字段外,基本上没什么有趣的字段。
如果你处理过 Win32 资源,你可能知道资源可以通过 ID 或名称来标识。这个结构中有两个成员会告诉我们这些条目的数量,以及条目总数(NumberOfNamedEntries + NumberOfIdEntries),这在遍历所有条目时很有用。你可能已经猜到,条目就在 DirectoryEntries 数组中。DirectoryEntries 由一组 IMAGE\_RESOURCE\_DIRECTORY\_ENTRY 结构组成,其格式如下:\`\`\`
所以基本上这东西是在内部用于解析内部内容,对我们来说,既然我们处理的是一个包含资源的目录,这就说得通了,酷。
再来点!
接下来
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/137d3d2aebf0db9501f550b09baddf01c3e50bf703ca8d13386a7185a567f19a.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/045c3a8a2bab306f516cdc7e9a59954e3c1ff16efe4189273c50f54b9e84f3e6.png" alt=""><figcaption></figcaption></figure>
</div>
这基本上就是遍历目录中的每个资源,检查它是否为字符串类型。
说实话,我不知道他为什么要这么做,如果我错了,抱歉;如果没错,干杯!
下一个
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/a57c5815ee8b5498fed6d8dde92f48b9fc2bdfd2d4f4e231796ba96e5e28bea9.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/bd4513fdf1f8f165fc404b8462d6bc36644de4937e42d560d54f1a5f1c0e8799.png" alt=""><figcaption></figcaption></figure>
</div>
这里发生的是,我们加上一些偏移量,最终到达了中国研究人员所说的第二个资源表,正如你所见。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/8f6655fb8117abf8a4d38b5173d51e4bc3ce67b86803c57cce36d47b73cd1e76.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/ebdf5a9a749f2cb1b7a656be5f2d99419edfc9cd2a737e15c6845f2c008e3f11.png" alt=""><figcaption></figcaption></figure>
</div>
然后我们重复同样的过程来获取一些偏移量。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/faa12c96d0b22e6128ef1cdf14110c31cc9ebca89325f28a07fc6a0a064daeba.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/216539eaf48a407a7f691d10cca06679a545471c3e54f25fedb31c38f0c61a66.png" alt=""><figcaption></figcaption></figure>
</div>
并重复同样的过程,这次我们检查类型 VS\_VERSION\_INFO。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/ac2d2e82085f4ddbc3f5ae1ce35528dd8420924b0298c65db056d0837c3e4b6d.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/23788c642b82988c14654e9dac2afd2504cd22846be0487efce96cae4d6fe094.png" alt=""><figcaption></figcaption></figure>
</div>
那么 VS\_VERSION\_INFO 是什么鬼?嗯,微软(https://learn.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource) 说它 `Defines a version-information resource`,例如,我相信它只是说明 bootmgfrw.ef 的版本。
最后,如果我们找到了 VS\_VERSION\_INFO,我们重复同样的算法。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/4f00ecf2c2c9b32b2d25fcbf3c62e9bb45679df1390a085371f5166a70d67933.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/facd25069491b5d7826500e4f2c04cafe213191075012ddb4c1fa9a8bc97c4c5.png" alt=""><figcaption></figcaption></figure>
</div>
这一次有点不同,不同之处在于我们返回构建 ID :) 如我们所见。
那么结论是,这里到底发生了什么鬼?嗯,根据中国研究人员使用的名称(GetPeFileVersionInfo\_BuildNumber\_),我们可以得出结论:我们实际上获取的是引导加载程序的构建号,从第一张图可以看到。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/b28007dda110fc31d53233931ff077ac42fc81146d803b91999069e204020a4d.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/d19f1311c2ee242ced63da8334db384929ada3acae147009becc0dbf34d2b7fb.png" alt=""><figcaption></figcaption></figure>
</div>
在这里我们看到已加载到内存中的引导加载程序。
在第二张图中我们看到
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/2000f17c51ccc65267b46ac2c1d191bd033b3bc6e6b655973423dbbd8a1fc77e.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/7c47fe45fe4b422618a400f2c26c28cd33fea9416f2b24086a2c852d08f1614b.png" alt=""><figcaption></figcaption></figure>
</div>
rcx 中有一个整数,可能是构建号或 pefileversion。
而第三张图
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/41729b1d4a4701480d64f058f421cd2ad8a8b9b871bf17675fd7b537cec3dd85.png" alt="3">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/d2b17725eeb9af2336d725be9c556565fba221fd2a3517912c942f411f9ed33b.png" alt=""><figcaption></figcaption></figure>
</div>
我们可以推测它可能是构建号,因为 ebx 会被移入 rax :)
作为对这个函数的最后一句评价:哇,惊人的工程。
\=============================================================================
现在进入下一个挑战 :) 根据上一阶段的输出,我们要么将 v10 设为 sub\_180001D80,要么设为 sub\_180001D48,如下所示。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/33581031e03f29b5997d46b708c467bfd49ce58625e99381dd28c109cadae7d3.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/6647119081cffb664dcca365392271398b2572ee04c24a0456141f33cbaaeecf.png" alt=""><figcaption></figcaption></figure>
</div>
在我们的例子中,v10=sub\_180001D80
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/2af043a1449f6bf10e2925f06697438716976d24a1539a61ab42c4301c4a8435.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/0769d4dff6e1da51acaf9c5bac3a9a9b4b446aae9d7c593e7e54d865ec13c2ea.png" alt=""><figcaption></figcaption></figure>
</div>
\=============================================================================
然后我们在我们的引导加载程序管理器与那个字节数组之间做一次 strcmp。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/59164794afc69304133f0a4c008b9e4755671e8d57bd8dc4ea5a2b2e6ae54164.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/03b00f7c18ff7db73dfd5253f02d6966955e38732fa7d1cfb4840366c17355a1.png" alt=""><figcaption></figcaption></figure>
</div>
我想在这里稍作停留,你可能已经猜到了,我在中国研究人员的博客文章中看到了一些有趣的东西。他把那个字节数组叫做 SigImgArchStartBootApplication。那么 SigImgArchStartBootApplication 是什么鬼,它属于谁,为什么这个数组会叫这个名字(migos)。所以如果我们在谷歌(gulugulu)上搜索 SigImgArchStartBootApplication,什么也找不到。现在考虑到当前上下文,我们使用 Windows 的引导加载程序管理器,让我们在 IDA 中打开它。我们转到 C:\Windows\Boot\EFI,在 IDA 中打开二进制文件并搜索 SigImgArchStartBootApplication,什么也没有。我们搜索 ImgArchStartBootApplication,然后看到了
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/1ad42d5236e8ca4da08688fe3d4c72e6b3de01fdce633a1bdaa870de421cdc40.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/d6e211aa12b37dfba0244d48516a08703248484c070f8d7ffcf0a28f08e8a833.png" alt=""><figcaption></figcaption></figure>
</div>
所以 ImgArchStartBootApplication .... 这狗在干嘛...!?嗯...啊...我要从 `@_xeroxz` 那里偷一段话(去关注他,你要是没关注他的作品,你在搞什么....)基本上他在一篇文章中说:`bootmgfw.ImgArchStartBootApplication between windows versions 2004-1709 is invoked to start winload.efi`,从他的图片中也可以看到(https://guidedhacking.com/threads/hyper-v-hacking-framework-works-on-every-version-of-windows-10-2004-1511-amd-intel.16251/)
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/4967559cb05e49785841a2dc216e13fc0b94fe8be8f17d9bceaf1914722156ff.jpg" alt="1603213912596">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/4967559cb05e49785841a2dc216e13fc0b94fe8be8f17d9bceaf1914722156ff.jpg" alt=""><figcaption></figcaption></figure>
</div>
如果这还不够清楚,在一篇文章()中我们看到:`ImgArchStartBootApplication to catch the moment when the Windows OS loader (winload.efi) is loaded in the memory but still has not been executed`(https://rustrepo.com/repo/rusty-bootkit--uefi-bootkit-in-rust)
酷,那么 strcmp 和 ImgArchStartBootApplication 有什么关系?嗯,让我们仔细看看 IDA,很快就会揭示答案。如果我们在引导加载程序代码中搜索字节 41 b8 09,我们很快就会遇到罪魁祸首。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/91050c01f95eb88488cbc575c21af7f453228eeb13c665b94d415eecf5682a55.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/7239d85f2dc9e220a2eab80f79b5db9705285a17c074f85958c041d122bfaf8e.png" alt=""><figcaption></figcaption></figure>
</div>
如果我们将内存中的引导加载程序映像字节与该字节签名匹配,就执行 sub\_180002398。
当然,如我们所见,我们找到了该模式,在 eax 中得到了字节所在的内存区域,然后安全地继续运行 sub\_180002398。
\=============================================================================
sub\_180002398
“汇编视角”
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/5aa624d92161499019e7ce6597bf46853c59ba89d37fec483a8ac7ee168ddbeb.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/afbf940a1eb535d73dce744606cc63c568c2b447c9cf85034c422a8e900859f4.png" alt=""><figcaption></figcaption></figure>
</div>
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/6468513a220946bbccfab53328c056a620755e1fe94ad3e36d8c8958ed777794.png" alt=""><figcaption></figcaption></figure>
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/c5c36e7c29f1cdea6f42d55d7605ad3c2044d66397c867c221da6c0bf74f8d6a.png" alt=""><figcaption></figcaption></figure>
“伪代码视角”
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/5b1d8a264dad55f34a0cc958a2f4a26014a31987af5b66754e754589ea085097.png" alt="3">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/1d3db7165e0f71a3fa76862ba8afb3869f821f59db6fc5a5c5e99d5002ecfba1.png" alt=""><figcaption></figcaption></figure>
</div>
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/ae65504884fa2881cb50af2f88e846a0ad029c9431ee6296b28993de8f2e4a0d.png" alt=""><figcaption></figcaption></figure>
那么这狗在干嘛?老实说,它做了一些计算、一些加减法,没什么真正重要的?为什么?因为它没那么有趣。我们感兴趣的是从函数返回后会发生什么。我们看到 rax
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/4eae862955421b2b1629c120bb09970714cf085e9255590d6cfa36a77bbc69e9.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/43917bf46096449ad92172baa282fd3e70ec0bec67fec2bbf2a276285d878b52.png" alt=""><figcaption></figcaption></figure>
</div>
好的,酷,但我还是不明白。好吧,rax = 0x5eec108,它指向 0x48c48b48,好,然后呢?我当时和你一样困惑,所以我再次回到中国研究人员的博客。那位研究人员描述这里发生的事情是:它回到了 ImgArchStartBootApplication 函数的开头。但他到底是怎么想到的?嗯,如前所述,rax =\ 0x48c48b48,如果我们检查 booloadermnfr.efi,我们会看到
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/2dbe283fa0b0dd9cf837e79f1f42eaeb6dcd61252e76979c0a4af3cf2f68e8d8.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/624e1927cac5a0011d6fabea0f2ce4740b8dfd14102509656d6e5c0395a9c08d.png" alt=""><figcaption></figcaption></figure>
</div>
这与 0x5eec108 中的字节序列完全相同。好的,现在这很酷 :)
请参考 sub\_180002398.py,看看我模拟此行为的失败尝试 :)
\=============================================================================
酷,接下来?
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/432ad1832fa35743bc5676f7f8362ad9e2043e17387121136a1f6dfe1bdf16f1.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/aab798c5c80621b1c367ea6eecfff0d7caf9ecc368ae2fe2e947538e4f0e2b0c.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/f04ebd9c650ddadb5553e4f2c43c72af8a5ccbef372a4dbdc811600e9583b0c9.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/3411e1620450739ea98f6db634346748487377106547324591b148cefe358df2.png" alt=""><figcaption></figcaption></figure>
</div>
接下来发生的是 RaiseTPL。好的,那这是做什么的?提高当前执行任务的优先级,并返回其先前的优先级级别。在我们的例子中,它将作为最高执行权限运行。
接下来,我们调用我称之为 patch\_something 的函数,它看起来像这样
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/0a31b0c154406b340e9bb044c4211afd6494bcac6ec197c99a10a6d0effb6a31.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/3fcff1b34a5d9af3cdd70e4dac1188abd3edd13e74d11e4f0bba5d667d4f53a1.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/c2f9bb8ca36961def04d4a3111a7d4213bb219949d13094b525a1a906ab788f7.png" alt="2">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/accdd93e3428b9b03132eafceda3ec5e1cfda742a978f1cd7f2c6bcf1904ceaa.png" alt=""><figcaption></figcaption></figure>
</div>
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/e0f6c806d296e47269b9908ceab7b90954bbd219d3d9c13462ebee8344f854f3.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/1ed164077989d04273ae3b40ec6daf7cf9b93ec42d917533b1e9758525231b7b.png" alt=""><figcaption></figcaption></figure>
</div>
因此,从静态分析中我们可以看出,这就是所谓的 hooking。:) 所以基本上它修补了 ImgArchStartBootApplication 的字节,使其指向 sub\_180001D80,并将 ImgArchStartBootApplication 的原始函数保存到 byte\_180015C78。
正如我们所见,它变成了完全对应的 sub\_180001D80。
<div>
<img src="https://assets.kitploit.com/production/public/readmes/44336/6c48bf4c9126a9a1466493aed943113cc27b4bbf9f7d68cdd251d0e14303ad8a.png" alt="1">
<figure><img src="https://assets.kitploit.com/production/public/readmes/44336/633d84a62c16dba91229fe6bb73ed0318719fc06f63f7f2a9d19adba0d0e8cfe.png" alt=""><figcaption></figcaption></figure>
</div>
接下来我们重置权限,然后从那里把控制权交给 boomgrfw.efi :)
所以这正式标志着分析的第一半完成 :) 下一部分,我们将学习如何进一步调试 sub\_180001D80 和 boomgrfw.efi(在我们的例子中是 winload.efi)。所以请坐稳,直到我学会如何为分析的第二部分准备环境。
\=============================================================================
现在进入分析的第二半部分.... 我们如何调试 boomgrfw.efi?