Shellcode: Windows on ARM64 / AArch64
介绍 早在2018年10月,我就想尝试在Windows上编写ARM汇编。当时我只能找到一台在2012年10月发布的、运行Windows RT的Surface平板电脑。Windows RT(现已废弃)是专为32位ARMv7架构设计的Windows 8版本。到了2013年夏天,它被认为是一次商业上的失败。 对于开发者而言,虽然可以通过USB或网络从另一台机器编译二进制文件并在平板上运行,但除非你愿意获取开发者许可证,否则需要借助越狱漏洞才能实现。由于限制太多,我的注意力转向了运行在Raspberry Pi4上的Linux系统。 根据我的了解,2015年发布的Windows 10 for ARMv7相较于Windows RT有了显著的改进。虽然对开发者的限制依然存在,但至少微软提供了对x86应用程序的模拟支持。 如今,我终于有了一台运行Windows 11的ARM64设备,之前困扰各版本的问题都已不复存在。开发者获得了完整的原生支持,包括Visual Studio 2022,以及一个可以运行Ubuntu或Debian的Linux子系统(如果你希望为Linux编写ARM64应用程序的话)。或许最棒的一点是,它能够同时模拟x86架构的32位和64位应用程序。
工具链 要支持Windows on ARM,你至少有以下三种选择:
- Visual Studio 2022
- LLVM-MinGW
- flat assembler g
MSVC和LLVM-MinGW最适合C/C++开发。我个人更喜欢GNU Assembler (as),而不是微软提供的ARM Macro Assembler (armasm64),但两者的主要问题在于缺乏对宏的支持。armasm64支持ARM文档中记录的大部分指令,但似乎存在一些限制。 据我所知,ARMASM完全不支持结构体,这使得用汇编语言编写程序变得非常困难。这也是GNU Assembler的一个问题,唯一的解决方法是为每个字段使用带有硬编码偏移量的符号名称。 不过,还是有希望的。尽管flat assembler g (FASMG) 由Tomasz Grysztar开发,并不直接支持ARM架构,但它是一个可适配的汇编引擎,“能够成为任何CPU架构的汇编器”。FASMG的包含文件中提供了通过宏实现的ARM64指令集,这正是我在本文中决定用于简单PoC(概念验证)的工具。 一旦你设置好FASMG,将asmFish中的AARCH64宏复制到包含目录中。我自己在fasm根目录下从命令提示符执行的批处理文件如下:
|
|
Thomas也提供了一个ARM64示例供入门参考。
调用约定 Windows在子程序调用上使用与Linux相同的约定。然而,系统调用的调用方式不同:Linux使用x8寄存器来保存系统调用ID,而Windows则将ID嵌入到SVC指令本身中。
| 寄存器 | 是否易失? | 角色 |
|---|---|---|
| x0 | 是 | 参数/暂存寄存器1,结果寄存器 |
| x1-x7 | 是 | 参数/暂存寄存器2-8 |
| x8-x15 | 是 | 暂存寄存器。也用作参数。 |
| x16-x17 | 是 | 过程内调用暂存寄存器 |
| x18 | 否 | 平台寄存器:在内核模式下指向当前处理器的KPCR;在用户模式下指向TEB |
| x19-x28 | 否 | 暂存寄存器 |
| x29/fp | 否 | 帧指针 |
| x30/lr | 否 | 链接寄存器 |
| x31/zxr | 否 | 零寄存器 |
你好,世界!(控制台) 最初,我使用的是ARMASM,所以下面的例子只是展示了如何创建一个简单的控制台应用程序。
|
|
这里还有一个简单的GUI版本。FASMG版本可以在这里找到。
你好,世界!(GUI)
|
|
符号名称
|
|
结构体和联合体 FASMG提供了宏来支持Borland的Turbo Assembler或微软的Macro Assembler所支持的结构体和联合体。
|
|
COM接口 Shellcode使用IStream对象从HTTP请求中读取数据。FASMG提供了声明接口的宏。还有comcall和cominvk宏来调用接口方法。我在这里决定不使用它们。正如之前关于执行.NET程序集所指出的那样,接口本质上就是包含函数指针的结构体。
|
|
局部变量 FASMG不直接支持局部变量。但你可以定义一个包含你变量的结构体来实现。
|
|
在程序或子程序的入口处,从堆栈指针中减去结构体的大小(按16字节对齐)。
|
|
当你需要寻址一个变量时,可以使用ADD指令来访问偏移量。
|
|
访问存储在var_tbl.pStream中的值:
|
|
宏 FASMG最强大的功能之一是其对宏的支持。完全可以用宏来实现像SHA256、SHA512和SHA3这样的加密哈希算法。以下代码完全无法展示FASMG的全部潜力。
|
|
线程环境块 (TEB) xpr是x18寄存器的别名。如上文整数寄存器表所述,对于用户模式应用程序,它包含一个指向TEB的指针。AMD64使用的每个偏移量很可能也适用于ARM64。不过,更安全的做法是检查调试符号。 系统调用 对于x86,系统调用号放在累加器(EAX/RAX)中,但对于ARM64,它被嵌入到SVC操作码本身中,而且似乎没有替代方法(至少据我所知没有)。要构建一个新的系统调用存根,需要使用NtAllocateVirtualMemory并手动编码指令。 HTTP下载 以下代码使用URLOpenBlockingStream从网上下载一个shellcode并在内存中执行。
|
|
扩展阅读
- 如何在Windows上设置fasmg
- Project Chameleon
- Jack-in-the-Cache:通过修改X86到ARM翻译缓存实现的新代码注入技术
- Windows ARM64上的系统调用分发
- 在Windows 10 ARM64上运行x64:这究竟是如何工作的?
- Windows 10 on ARM – x86模拟
- CVE-2021-21224
- WoW64内部机制:重新发现ARM上的“天堂之门”