【发布时间】:2019-02-19 23:55:21
【问题描述】:
我正在尝试使用 GAS 语法创建一个汇编程序,该程序可以在 x86-64 架构上以与位置无关的方式从 .data 部分访问它的变量,并强制执行 32bit 架构和 IS (@ 987654326@ 而不是 %rip)。
无论我尝试什么寄存器,我得到的最好结果都是Segmentation fault: 11,即使是用于访问我根本无法访问的 EIP,也就是访问 SF。最好的结果,因为这至少告诉我除了“嗯,它不会做”之外的其他东西。
我在 macOS 10.13.6 mid 2010 Intel Core 2 Duo 上使用gcc 编译文件(这可能是clang 的原因):
$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
并将一些选项传递给链接器:
gcc -m32 -Wl,-fatal_warnings,-arch_errors_fatal,-warn_commons,-pie test.s
ld:警告:PIE 已禁用。在代码签名的 PIE 中不允许使用绝对寻址(可能是 -mdynamic-no-pic),但在来自 /whatever.../test-a07cf9.o 的 _main 中使用。要修复此警告,请勿使用 -mdynamic-no-pic 编译或使用 -Wl,-no_pie 链接 ld:致命警告引发的错误(-fatal_warnings) clang:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用) 1
test.s
.text
.global _main
_main:
xor %eax, %eax
xor %ebx, %ebx
# lea var1(%esi/edi/ebp/esp), %ebx # can't compile, not PIE
# lea var1(%eip), %ebx # segfault, obvs
# lea (%esp), %ebx # EBX = 17
# lea (%non-esp), %ebx # segfault
# lea 0(%esi), %ebx # segfault
# lea 0(%edi), %ebx # segfault
# lea 0(%ebp), %ebx # EBX = 0
# lea 0(%esp), %ebx # EBX = 17
# lea 0(%eip), %ebx # segfault, obvs
movl (%ebx), %eax
ret
.data
var1: .long 6
.end
我使用./a.out; echo $? 运行它以检查最后来自ret 的EAX 值。
我查看了各种来源,但主要是英特尔语法或这些问题之一 - 1、2、3。我试图反汇编我能想到的最简单的 C 示例,即全局变量 + return 来自 main() - gcc -S test.c -fPIE -pie -fpie -m32:
int var1 = 6;
int main() { return var1; }
这基本上导致:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 13
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushl %ebp
Lcfi0:
.cfi_def_cfa_offset 8
Lcfi1:
.cfi_offset %ebp, -8
movl %esp, %ebp
Lcfi2:
.cfi_def_cfa_register %ebp
pushl %eax
calll L0$pb
L0$pb:
popl %eax
movl $0, -4(%ebp)
movl _var1-L0$pb(%eax), %eax
addl $4, %esp
popl %ebp
retl
.cfi_endproc
## -- End function
.section __DATA,__data
.globl _var1 ## @var1
.p2align 2
_var1:
.long 6 ## 0x6
.subsections_via_symbols
这显然使用 MOV 作为 LEA 并且与我的指令几乎相同,除了 -L0$pb 部分应该是 +/- 类似 _var1 的地址 - L0$pb 的地址以进入 .data 部分。
然而,当我尝试使用 var1 和 _main 标签的相同方法时,什么都没有:
.text
.global _main
_main:
xor %eax, %eax
xor %ebx, %ebx
#movl var1-_main(%ebp), %eax # EAX = 191
#movl var1-_main(%esp), %eax # EAX = 204
#movl var1-_main(%eax), %eax # segfault
ret
.data
var1: .long 6
.end
任何想法我做错了什么?
编辑:
我设法从反汇编的 C 示例中删除了任何不必要的东西,并最终得到了这个:
.text
.global _main
_main:
pushl %ebp
pushl %eax
calll test
test:
popl %eax
/* var1, var2, ... */
movl var1-test(%eax), %eax
addl $4, %esp
popl %ebp
retl
/**
* how var1(label) - test(label) skips this label
* if it's about address subtracting?
*/
blobbbb:
xor %edx, %edx
.data
var1: .long 6
var2: .long 135
这对我来说没有多大意义,因为根据this guide,调用者应该 1)将参数推送到堆栈(无)2)call 标签和 被调用者实际上应该使用 ESP、EBP 和其他寄存器。另外,为什么我什至需要一个中间标签,或者更好地说,没有它有什么办法吗?
【问题讨论】:
-
您是否尝试过检查 C 编译器的输出并这样做?
-
@fuz 是的,我可以用一个相当小的例子来重现它,push ebp、push eax、调用中间标签、pop eax、在该标签中使用 mov 或 lea、pop ebp、ret。但我认为必须有更好的方法。当我到达 PC 时,我会编辑问题。
-
@fuz 我用一个简单的气体 asm 示例编辑了这个问题,它可以访问 PIE 变量,但在我看来,这很奇怪,而且不符合我在互联网上找到的调用者/被调用者指南。
-
clang 输出中的函数调用是一个假函数调用,用于获取堆栈上指令指针的内容。这会立即在下一条指令中弹出,以将
test的实际地址转换为eax。您会对解释那里实际情况的答案感到满意吗?您能否告诉我编译器生成的实际代码,而不是经过编辑的版本(因为我认为其中缺少某些内容)。 -
@fuz 再次编辑。是的,差不多就行了。另外,如果您有时间,请解释有关上一个示例中遗漏标签的评论或粘贴链接,我会从中进行管理。 :) 我仍然不确定 PIE 和变量是否除了假调用之外没有其他方法,嗯..
标签: assembly x86 gnu-assembler position-independent-code