movdqa/movdqu 指令如何将错误的值写入内存?

问题描述

我正在尝试使用英特尔 AES 指令集实现 AES 加密,但在将 xmm 寄存器的内容移动到内存时遇到了一个奇怪的问题。我正在尝试将 %xmm1 的内容移动到 %rsi 中的堆栈地址。这是我的代码

movdqa (%rdi),%xmm1
movdqa %xmm1,(%rsi)

(%rdi) 的内容按预期移动到 xmm1,但在将它们移动到 (%rsi) 时,第一个 dword 被清零,并且不复制最后一个 dword:

(lldb) mem read $rdi -c 0x10
0x7ffeefbff3a0: 30 28 00 00 00 00 00 00 42 42 42 42 41 41 41 41  0(......BBBBAAAA
(lldb) mem read $rsi -c 0x10
0x7ffeefbff2f0: 00 00 00 00 00 00 00 00 20 71 4e 00 00 00 00 00  ........ qN.....
(lldb) s
(lldb) reg read xmm1
    xmm1 = {0x30 0x28 0x00 0x00 0x00 0x00 0x00 0x00 0x42 0x42 0x42 0x42 0x41 0x41 0x41 0x41}
(lldb) s
(lldb) mem read $rsi -c 0x10
0x7ffeefbff2f0: 00 00 00 00 00 00 00 00 42 42 42 42 00 00 00 00  ........BBBB....

即使内存是 16 字节对齐的,我也尝试了 movdqu 指令并得到了相同的结果。有没有人知道这种行为的原因,或者有解决方法的想法?

这是我的代码,问题出现在 copy_aes_key_schedule 的第 3 行。我正在用 Xcode 编译和调试它。

在 main.c 中:

#include <stdio.h>

extern void encrypt_function_call(uint64_t functionID,uint64_t returnAddress,uint64_t opcodes,uint8_t result[0x10]);

int main(int argc,const char * argv[]) {
    // insert code here...
    
    uint32_t encrypted[4];
    encrypt_function_call(0xd67f1567,0x2830,0x4141414142424242,encrypted);
    
    return 0;
}

在 e.s 中:

.globl _encrypt_function_call

key_expansion_128:
pshufd $0xff,%xmm2,%xmm2
vpslldq $4,%xmm1,%xmm3
pxor %xmm3,%xmm1
vpslldq $4,%xmm1
pxor %xmm2,%xmm1
movdqu %xmm1,(%rcx)
add $0x10,%rcx
ret

copy_aes_key_schedule:
push %rcx
movdqa (%rdi),(%rsi)
lea 0x10(%rsi),%rcx // Untested from here on
aeskeygenassist $1,%xmm2
call key_expansion_128
aeskeygenassist $2,%xmm2
call key_expansion_128
aeskeygenassist $4,%xmm2
call key_expansion_128
aeskeygenassist $8,%xmm2
call key_expansion_128
aeskeygenassist $0x10,%xmm2
call key_expansion_128
aeskeygenassist $0x20,%xmm2
call key_expansion_128
aeskeygenassist $0x40,%xmm2
call key_expansion_128
aeskeygenassist $0x80,%xmm2
call key_expansion_128
aeskeygenassist $0x1b,%xmm2
call key_expansion_128
aeskeygenassist $0x36,%xmm2
call key_expansion_128
pop %rcx
ret

_encrypt_function_call:
push %rbp
mov %rsp,%rbp
push %r12
push %r13
push %r14
push %r15
mov %rcx,%r15
lea -0x30(%rbp),%r10 // data
mov %rdi,(%r10)
rdrand %r12
mov %r12,8(%r10)
rdrand %r12d
mov %r12d,4(%r10)
lea -0x40(%rbp),%rdi // key
mov %rsi,(%rdi)
mov %rdx,8(%rdi)
lea -0xf0(%rbp),%rsi
call copy_aes_key_schedule
call _abort // untested from here on
movdqu (%r10),%xmm15
movdqu (%rsi),%xmm0
pxor %xmm0,%xmm15
movdqu 0x10(%rsi),%xmm0
aesenc %xmm0,%xmm15
movdqu 0x20(%rsi),%xmm15
movdqu 0x30(%rsi),%xmm15
movdqu 0x40(%rsi),%xmm15
movdqu 0x50(%rsi),%xmm15
movdqu 0x60(%rsi),%xmm15
movdqu 0x70(%rsi),%xmm15
movdqu 0x80(%rsi),%xmm15
movdqu 0x90(%rsi),%xmm15
movdqu 0xa0(%rsi),%xmm0
aesenclast %xmm0,%xmm15
movdqu %xmm15,(%r15)
pop %r15
pop %r14
pop %r13
pop %r12
pop %rbp
ret

解决方法

经过进一步调查和更奇怪的行为,我通过将保存 AES 密钥调度结果的堆栈变量的大小增加到 0xf0 字节来解决问题,即使实际密钥调度仅为 0xb0 字节。