⬑
ASLR时代的用户态汇编寻址
2024-08-27为防止恶意程序攻击已知地址,现代编译器默认会开启地址空间随机配置(ASLR)。用汇编语言写程序的时候,ASLR会带来一些额外的麻烦。
这里我们用nasm汇编器。
例如下面的代码:
;; hello.asm
extern printf
global main
section .data
fmtStr db "The answer is %d.", 0xa, 0
ans dd 42
section .bss
section .text
main:
push rbp
mov rbp, rsp
mov rdi, fmtStr
mov rsi, [ans]
mov rax, 0
call printf
mov rsp, rbp
pop rbp
ret
上述代码所等价的C语言代码为:
#include <stdio.h>
const int ans = 42;
int main() {
printf("The answer is %d.\n", ans);
return 0;
}
但是如果尝试用下面的命令编译:
nasm -f elf64 -g main.asm
gcc main.o -o main
会出现如下报错信息:
/usr/bin/ld: main.o: relocation R_X86_64_32S against `.data' can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
这是因为地址空间随机分配之后,链接器无法再直接确定某段数据或者函数代码所在的位置。如果想要编译这段代码,必须在链接时关闭ASLR:
nasm -f elf64 -g main.asm
gcc main.o -o main -no-pie
但是这样会引入安全问题。实际上,在ASLR时代,寻址都应该是相对寻址。
对于非ASLR的程序,如果要获取地址,原本只需要:
mov rdi, fmtStr
现在则需要:
lea rdi, [rel fmtStr]
如果要从内存中加载数据,原本只需要:
mov rsi, [ans]
现在则需要改成:
mov rsi, [rel ans]
如果要调用外部的函数,原本只需要直接调用:
call printf
现在则需要在Procedure Lookup Table (PLT)中查找函数地址:
call printf wrt ..plt
因此,上面的程序需要修改为:
;; hello.asm
extern printf
global main
section .data
fmtStr db "The answer is %d.", 0xa, 0
ans dd 42
section .bss
section .text
main:
push rbp
mov rbp, rsp
lea rdi, [rel fmtStr]
mov rsi, [rel ans]
mov rax, 0
call printf wrt ..plt
mov rsp, rbp
pop rbp
ret
这样就可以用gcc默认配置链接了:
nasm -f elf64 -g main.asm
gcc main.o -o main
运行结果为:
The answer is 42.
Email: i (at) mistivia (dot) com