此问题在此处已有答案:
Save CPU registers to variables in GCC(1个答案)
四年前就关门了。
这是我的C程序......我试着打印出ESP,EBP和EIP。
# include <stdio.h>
int main() {
register int i asm("esp");
printf("%#010x <= $ESP\n", i);
int a = 1;
int b = 2;
char c[] = "A";
char d[] = "B";
printf("%p d = %s \n", &d, d);
printf("%p c = %s \n", &c, c);
printf("%p b = %d \n", &b, b);
printf("%p a = %d \n", &a, a);
register int j asm("ebp");
printf("%#010x <= $EBP\n", j);
//register int k asm("eip");
//printf("%#010x <= $EIP\n", k);
return 0;
}
我对超感知觉和外压没有问题
user@linux:~# ./memoryAddress
0xbffff650 <= $ESP
0xbffff654 d = B
0xbffff656 c = A
0xbffff658 b = 2
0xbffff65c a = 1
0xbffff668 <= $EBP
user@linux:~#
但是当我尝试输入EIP代码时,在编译它时出现以下错误。
user@linux:~# gcc memoryAddress.c -o memoryAddress -g
memoryAddress.c: In function ‘main’:
memoryAddress.c:20:15: error: invalid register name for ‘k’
register int k asm("eip");
^
user@linux:~#
这个代码有什么问题吗?
register int k asm("eip");
printf("%#010x <= $EIP\n", k);
是否可以通过C编程打印出EIP值?
如果是,请让我知道如何做。
更新
我已经测试了这里的代码...
user@linux:~/c$ lscpu
Architecture: i686
CPU op-mode(s): 32-bit
Byte Order: Little Endian
谢谢@Antti Haapala和其他人的帮助。代码工作...但是,当我加载到GDB时,EIP值不一样。
(gdb) b 31
Breakpoint 1 at 0x68f: file eip.c, line 31.
(gdb) i r $eip $esp $ebp
The program has no registers now.
(gdb) r
Starting program: /home/user/c/a.out
0x00000000 <= Low Memory Address
0x40055d <= main() function
0x4005a5 <= $EIP 72 bytes from main() function (start)
0xbffff600 <= $ESP (Top of the Stack)
0xbffff600 d = B
0xbffff602 c = A
0xbffff604 b = 2
0xbffff608 a = 1
0xbffff618 <= $EBP (Bottom of the Stack)
0xffffffff <= High Memory Address
Breakpoint 1, main () at eip.c:31
31 return 0;
(gdb) i r $eip $esp $ebp
eip 0x40068f 0x40068f <main+306>
esp 0xbffff600 0xbffff600
ebp 0xbffff618 0xbffff618
(gdb)
下面是新代码
# include <stdio.h>
# include <inttypes.h>
int main() {
register int i asm("esp");
printf("0x00000000 <= Low Memory Address\n");
printf("%p <= main() function\n", &main);
uint32_t eip;
asm volatile("1: lea 1b, %0;": "=a"(eip));
printf("0x%" PRIx32 " <= $EIP %" PRIu32 " bytes from main() function (start)\n",
eip, eip - (uint32_t)main);
int a = 1;
int b = 2;
char c[] = "A";
char d[] = "B";
printf("%#010x <= $ESP (Top of the Stack)\n", i);
printf("%p d = %s \n", &d, d);
printf("%p c = %s \n", &c, c);
printf("%p b = %d \n", &b, b);
printf("%p a = %d \n", &a, a);
register int j asm("ebp");
printf("%#010x <= $EBP (Bottom of the Stack)\n", j);
printf("0xffffffff <= High Memory Address\n");
return 0;
}
3条答案
按热度按时间gwbalxhn1#
请先阅读QA Reading program counter directly-从那里我们可以看到,* 没有 *
mov
命令可以直接访问EIP/RIP
,因此您不能使用register asm
来访问它。相反,您可以随时使用这些技巧。在64位模式下最简单,使用以获取64位指令(感谢Michael Petch在此处指出标签可以与
lea
一起使用。演示:
然后
证明它是正确的:
对于32位代码,您似乎可以使用带有标签的
lea
--但这不适用于64位代码。然后
证明它是正确的:
(in 32位版本有更多的
lea
命令,但这一个是“在这里加载我的常量地址”,然后将由动态链接器在加载exe时进行纠正)。fykwrbwg2#
EIP不能直接读取。RIP可以,使用
lea 0(%rip), %rax
,但它不是通用寄存器。您可以直接使用代码地址,而不是从寄存器阅读地址。
如果您将其编译为PIC(位置无关代码),编译器将通过阅读EIP或RIP来获取函数的运行时地址。您不需要为此使用内联asm。
对于非功能的地址,GNU C allows labels as values。
在Godbolt编译器资源管理器上使用不带
-fPIE
的进行编译,以生成与位置无关的代码,我们得到:如果没有
-fPIE
,则地址是链路时间常量(适合32位常量),因此我们得到你是否从标签中得到一个有意义的地址取决于编译器对你放置标签的代码进行优化的积极程度。如果你甚至得到标签地址,那么把标签放在某个地方可能会抑制优化(比如自动矢量化),但是IDK。也许只有当你实际上有一个
goto
时,它才是有害的。hyrbngr73#
你可以阅读
rip
与另一个小黑客,如果你有兴趣.这里你的完整代码读rip
太:这是一个很有趣的方法。你只需要
call
下一条流水线。现在因为返回地址是堆栈中的rip
,你可以通过一条pop
指令从堆栈中检索它。:)更新日期:
使用此方法的主要原因是数据注入。请参见以下代码:
此方法可以将数据注入到代码段中(这对代码注入很有用)。如果编译并运行,则会看到以下输出:
您已经使用
rip
作为数据的占位符。我个人喜欢这种方法,但它可能会产生效率影响,如评论中所提到的。我已经在64位Ubuntu bash for Windows(Linux子系统for Windows)中测试了这两种代码,两者都能正常工作。
更新2:
请务必阅读关于red zones的评论。非常感谢michael提到这个问题并提供了一个例子。:)如果你需要使用这个代码而不出现红区问题,你需要如下所示编写它(来自michael的示例):