linux 如何打印C语言EIP地址?[duplicate]

a5g8bdjr  于 2022-11-02  发布在  Linux
关注(0)|答案(3)|浏览(164)

此问题在此处已有答案

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;
}
gwbalxhn

gwbalxhn1#

请先阅读QA Reading program counter directly-从那里我们可以看到,* 没有 * mov命令可以直接访问EIP/RIP,因此您不能使用register asm来访问它。相反,您可以随时使用这些技巧。在64位模式下最简单,使用

uint64_t rip;
asm volatile("1: lea 1b(%%rip), %0;": "=a"(rip));

以获取64位指令(感谢Michael Petch在此处指出标签可以与lea一起使用。
演示:


# include <stdio.h>

# include <inttypes.h>

int main(void) {
    uint64_t rip;
    asm volatile("1: lea 1b(%%rip), %0;": "=a"(rip));
    printf("%" PRIx64 "; %" PRIu64 " bytes from main start\n",
           rip, rip - (uint64_t)main);
}

然后

% gcc -m64 rip.c -o rip; ./rip
55b7bf9e8659; 8 bytes from start of main

证明它是正确的:

% gdb -batch -ex 'file ./rip' -ex 'disassemble main'
Dump of assembler code for function main:
   0x000000000000064a <+0>:     push   %rbp
   0x000000000000064b <+1>:     mov    %rsp,%rbp
   0x000000000000064e <+4>:     sub    $0x10,%rsp
   0x0000000000000652 <+8>:     lea    -0x7(%rip),%rax        # 0x652 <main+8>

对于32位代码,您似乎可以使用带有标签的lea--但这不适用于64位代码。


# include <stdio.h>

# include <inttypes.h>

int main(void) {
    uint32_t eip;
    asm volatile("1: lea 1b, %0;": "=a"(eip));
    printf("%" PRIx32 "; %" PRIu32 " bytes from main start\n",
           eip, eip - (uint32_t)main);
}

然后

% gcc -m32 eip.c -o eip; ./eip
5663754a; 29 bytes from main start

证明它是正确的:

% gdb -batch -ex 'file ./eip' -ex 'disassemble main'  
Dump of assembler code for function main:
   0x0000052d <+0>:     lea    0x4(%esp),%ecx
   0x00000531 <+4>:     and    $0xfffffff0,%esp
   0x00000534 <+7>:     pushl  -0x4(%ecx)
   0x00000537 <+10>:    push   %ebp
   0x00000538 <+11>:    mov    %esp,%ebp
   0x0000053a <+13>:    push   %ebx
   0x0000053b <+14>:    push   %ecx
   0x0000053c <+15>:    sub    $0x10,%esp
   0x0000053f <+18>:    call   0x529 <__x86.get_pc_thunk.dx>
   0x00000544 <+23>:    add    $0x1a94,%edx
   0x0000054a <+29>:    lea    0x54a,%eax

(in 32位版本有更多的lea命令,但这一个是“在这里加载我的常量地址”,然后将由动态链接器在加载exe时进行纠正)。

fykwrbwg

fykwrbwg2#

EIP不能直接读取。RIP可以,使用lea 0(%rip), %rax,但它不是通用寄存器。
您可以直接使用代码地址,而不是从寄存器阅读地址。

void print_own_address() {
    printf("%p\n", print_own_address);
}

如果您将其编译为PIC(位置无关代码),编译器将通过阅读EIP或RIP来获取函数的运行时地址。您不需要为此使用内联asm。

对于非功能的地址,GNU C allows labels as values

void print_label_address() {
    for (int i=0 ; i<1000; i++) {
        volatile int sink = i;
    }
  mylabel:
    for (int i=0 ; i<1000; i++) {
        volatile int sink2 = i;
    }
    printf("%p\n", &&mylabel);   // Take the label address with && GNU C syntax.

}

在Godbolt编译器资源管理器上使用不带-fPIE的进行编译,以生成与位置无关的代码,我们得到:


# PIE version:

    xor     eax, eax                   # i=0
.L4:                                   # do {
    mov     DWORD PTR -16[rsp], eax    #  sink=i
    add     eax, 1
    cmp     eax, 1000
    jne     .L4                        # } while(i!=1000);

    xor     eax, eax                   # i=0
.L5:                                   # do {
    mov     DWORD PTR -12[rsp], eax    # sink2 = i
    add     eax, 1
    cmp     eax, 1000
    jne     .L5                        # }while(i != 1000);

    lea     rsi, .L5[rip]           # address of .L5 = mylabel
    lea     rdi, .LC0[rip]          # format string
    xor     eax, eax                # 0 FP args in XMM regs for a variadic function
    jmp     printf@PLT              # tailcall printf

如果没有-fPIE,则地址是链路时间常量(适合32位常量),因此我们得到

mov     esi, OFFSET FLAT:.L5
    mov     edi, OFFSET FLAT:.LC0
    xor     eax, eax
    jmp     printf

你是否从标签中得到一个有意义的地址取决于编译器对你放置标签的代码进行优化的积极程度。如果你甚至得到标签地址,那么把标签放在某个地方可能会抑制优化(比如自动矢量化),但是IDK。也许只有当你实际上有一个goto时,它才是有害的。

hyrbngr7

hyrbngr73#

你可以阅读rip与另一个小黑客,如果你有兴趣.这里你的完整代码读rip太:


# include <stdio.h>

# include <inttypes.h>

int main()
{
    register uint64_t i asm("rsp");
    printf("%" PRIx64 " <= $RSP\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 uint64_t j asm("rbp");
    printf("%" PRIx64 " <= $RBP\n", j);

    uint64_t rip = 0;

    asm volatile ("call here2\n\t"
                  "here2:\n\t"
                  "pop %0"
                  : "=m" (rip));
    printf("%" PRIx64 " <= $RIP\n", rip);

    return 0;
}

这是一个很有趣的方法。你只需要call下一条流水线。现在因为返回地址是堆栈中的rip,你可以通过一条pop指令从堆栈中检索它。:)

更新日期:

使用此方法的主要原因是数据注入。请参见以下代码:


# include <stdio.h>

# include <inttypes.h>

int main()
{
    uint64_t rip = 0;

    asm volatile ("call here2\n\t"
                  ".byte 0x41\n\t" // A
                  ".byte 0x42\n\t" // B
                  ".byte 0x43\n\t" // C
                  ".byte 0x0\n\t"  // \0
                  "here2:\n\t"
                  "pop %0"
                  : "=m" (rip));
    printf("%" PRIx64 " <= $RIP\n", rip);
    printf("injected data:%s\n", (char*)rip);

    return 0;
}

此方法可以将数据注入到代码段中(这对代码注入很有用)。如果编译并运行,则会看到以下输出:

400542 <= $RIP
injected data:ABC

您已经使用rip作为数据的占位符。我个人喜欢这种方法,但它可能会产生效率影响,如评论中所提到的。
我已经在64位Ubuntu bash for Windows(Linux子系统for Windows)中测试了这两种代码,两者都能正常工作。

更新2:

请务必阅读关于red zones的评论。非常感谢michael提到这个问题并提供了一个例子。:)如果你需要使用这个代码而不出现红区问题,你需要如下所示编写它(来自michael的示例):

asm volatile ("sub $128, %%rsp\n\t"
              "call 1f\n\t"
              ".byte 0x41\n\t" // A
              ".byte 0x42\n\t" // B
              ".byte 0x43\n\t" // C
              ".byte 0x0\n\t"  // \0
              "1:\n\t"
              "pop %0\n\t"
              "add $128, %%rsp"

              : "=r" (rip));

相关问题