如何通过 gcc 从 x86 内联汇编中获取 EIP
How get EIP from x86 inline assembly by gcc
我想从下面的代码中获取EIP的值,但是编译不通过
命令:
gcc -o xxx x86_inline_asm.c -m32 && ./xxx
文件内容x86_inline_asm.c:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
unsigned int eip_val;
__asm__("mov %0,%%eip":"=r"(eip_val));
return 0;
}
如何使用内联汇编获取EIP的值,并在x86下编译成功。
如何修改代码并使用命令完成?
我不知道这个的 gcc 内联汇编语法,但是对于 masm:
call next0
next0: pop eax ;eax = eip for this line
在 Masm 的情况下,$ 表示当前位置,并且由于 call 是一个 5 字节指令,没有标签的替代语法是:
call $+5
pop eax
这听起来不太可能有用(相对于像 void *tmp = main
那样只获取整个函数的地址),但它是可能的。
只要得到一个标号地址,或者用.
(当前行的地址),让链接器操心把正确的立即数放到机器码里。所以你不是在架构上阅读EIP
,只是从即时阅读它当前拥有的价值。
asm volatile("mov $., %0" : "=r"(address_of_mov_instruction) );
AT&T 的语法是mov src, dst
,所以你写的是汇编的话就是一个跳转。
(在架构上,EIP = 指令在执行时的 end,因此可以说你应该做
asm volatile(
"mov f, %0 \n\t" // reference label 1 forward
"1:" // GAS local label
"=r"(address_after_mov)
);
我正在使用 asm volatile
以防此 asm 语句通过内联或其他方式在同一函数内多次重复。如果你想让每个案例得到一个不同的地址,它必须是volatile
。否则,编译器会假定此 asm 语句的所有实例都产生相同的输出。通常这样就可以了。
在 32 位模式下的架构上,您没有针对 LEA 的 RIP 相对寻址,因此实际读取 EIP 的唯一好方法是调用/弹出。 Reading program counter directly。它不是通用寄存器,因此您不能仅将其用作 mov
或任何其他指令的源或目标。
但实际上您根本不需要内联汇编。
Is it possible to store the address of a label in a variable and use goto to jump to it? 展示了如何使用 GNU C 扩展,其中 &&label
取其地址。
int foo;
void *addr_inside_function() {
foo++;
lab1: ; // labels only go on statements, not declarations
void *tmp = &&lab1;
foo++;
return tmp;
}
在函数之外,您无法安全地使用此地址进行任何操作;我把它作为一个例子返回,让编译器在 asm 中放置一个标签,看看会发生什么。如果该标签没有 goto
,它仍然可以非常积极地优化函数,但您可能会发现它可用作函数其他地方的 asm goto(...)
的输入。
但是无论如何,它编译on Godbolt到这个asm
# gcc -O3 -m32
addr_inside_function:
.L2:
addl , foo
movl $.L2, %eax
ret
#clang -O3 -m32
addr_inside_function:
movl foo, %eax
leal 1(%eax), %ecx
movl %ecx, foo
.Ltmp0: # Block address taken
addl , %eax
movl %eax, foo
movl $.Ltmp0, %eax # retval = label address
retl
所以 clang 加载全局,计算 foo+1 并存储它,然后在标签之后计算 foo+2 并存储它。 (而不是加载两次)。所以你仍然不能从任何地方有效地跳转到标签,因为它取决于 foo
在 eax
中的旧值,以及存储 foo+2
[= 所需的行为31=]
我想从下面的代码中获取EIP的值,但是编译不通过
命令: gcc -o xxx x86_inline_asm.c -m32 && ./xxx
文件内容x86_inline_asm.c:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
unsigned int eip_val;
__asm__("mov %0,%%eip":"=r"(eip_val));
return 0;
}
如何使用内联汇编获取EIP的值,并在x86下编译成功。 如何修改代码并使用命令完成?
我不知道这个的 gcc 内联汇编语法,但是对于 masm:
call next0
next0: pop eax ;eax = eip for this line
在 Masm 的情况下,$ 表示当前位置,并且由于 call 是一个 5 字节指令,没有标签的替代语法是:
call $+5
pop eax
这听起来不太可能有用(相对于像 void *tmp = main
那样只获取整个函数的地址),但它是可能的。
只要得到一个标号地址,或者用.
(当前行的地址),让链接器操心把正确的立即数放到机器码里。所以你不是在架构上阅读EIP
,只是从即时阅读它当前拥有的价值。
asm volatile("mov $., %0" : "=r"(address_of_mov_instruction) );
AT&T 的语法是mov src, dst
,所以你写的是汇编的话就是一个跳转。
(在架构上,EIP = 指令在执行时的 end,因此可以说你应该做
asm volatile(
"mov f, %0 \n\t" // reference label 1 forward
"1:" // GAS local label
"=r"(address_after_mov)
);
我正在使用 asm volatile
以防此 asm 语句通过内联或其他方式在同一函数内多次重复。如果你想让每个案例得到一个不同的地址,它必须是volatile
。否则,编译器会假定此 asm 语句的所有实例都产生相同的输出。通常这样就可以了。
在 32 位模式下的架构上,您没有针对 LEA 的 RIP 相对寻址,因此实际读取 EIP 的唯一好方法是调用/弹出。 Reading program counter directly。它不是通用寄存器,因此您不能仅将其用作 mov
或任何其他指令的源或目标。
但实际上您根本不需要内联汇编。
Is it possible to store the address of a label in a variable and use goto to jump to it? 展示了如何使用 GNU C 扩展,其中 &&label
取其地址。
int foo;
void *addr_inside_function() {
foo++;
lab1: ; // labels only go on statements, not declarations
void *tmp = &&lab1;
foo++;
return tmp;
}
在函数之外,您无法安全地使用此地址进行任何操作;我把它作为一个例子返回,让编译器在 asm 中放置一个标签,看看会发生什么。如果该标签没有 goto
,它仍然可以非常积极地优化函数,但您可能会发现它可用作函数其他地方的 asm goto(...)
的输入。
但是无论如何,它编译on Godbolt到这个asm
# gcc -O3 -m32
addr_inside_function:
.L2:
addl , foo
movl $.L2, %eax
ret
#clang -O3 -m32
addr_inside_function:
movl foo, %eax
leal 1(%eax), %ecx
movl %ecx, foo
.Ltmp0: # Block address taken
addl , %eax
movl %eax, foo
movl $.Ltmp0, %eax # retval = label address
retl
所以 clang 加载全局,计算 foo+1 并存储它,然后在标签之后计算 foo+2 并存储它。 (而不是加载两次)。所以你仍然不能从任何地方有效地跳转到标签,因为它取决于 foo
在 eax
中的旧值,以及存储 foo+2
[= 所需的行为31=]