使用写入系统调用程序集打印寄存器值

时间:2012-12-09 19:17:40

标签: assembly stack-overflow inline-assembly

.section .bss
        .lcomm stack, 4

.section .text

.globl _start
_start:
nop

movl %eip,$stack   --> How i can move ESP or any other registers value which will actually be ADDRESS , into STACK variable?

movl $4, %eax
movl $1, %ebx
movl $stack, %ecx
movl $4, %edx
int $0x80

movl $1, %eax
movl $0, %ebx
int $0x80

实际上我想使用WRITE系统调用来打印当前的ESP值,因为我将在我的C程序中将其用作内联汇编。

1 个答案:

答案 0 :(得分:1)

这个GNU C / C ++代码(GNU因为它用来检索当前stackpointer值的内联汇编样式)将打印stackpointer的当前值(十六进制)

static __inline__ void printESP(void) {
    static const hexdigit[] = "0123456789ABCDEF\n";
    uintptr_t SP;

    __asm__ __volatile__("mov %%esp, %0\n\t" : "=r"(SP));

    write(1, &hexdigit[SP >> 28], 1);
    write(1, &hexdigit[0xf & SP >> 24], 1);
    write(1, &hexdigit[0xf & SP >> 20], 1);
    write(1, &hexdigit[0xf & SP >> 16], 1);
    write(1, &hexdigit[0xf & SP >> 12], 1);
    write(1, &hexdigit[0xf & SP >> 8], 1);
    write(1, &hexdigit[0xf & SP >> 4], 1);
    write(1, &hexdigit[0xf & SP], 1);
    write(1, &hexdigit[16], 1);
}

由于static __inline__,gcc在大多数情况下(除非,实际上,如果使用函数指针)实际上不会创建此函数,而是将代码直接嵌入到调用点。

你可以构建一个更复杂的作品,它可以做类似的事情:

static __inline__ printESP(void)
{
    static const char hexdigit[] = "0123456789ABCDEF\n";
    uintptr_t SP;
    char buf[9];

    __asm__ __volatile__("mov %%esp, %0\n\t" : "=r"(SP));

    buf[0] = hexdigit[SP >> 28];
    buf[1] = hexdigit[0xf & SP >> 24];
    buf[2] = hexdigit[0xf & SP >> 20];
    buf[3] = hexdigit[0xf & SP >> 16];
    buf[4] = hexdigit[0xf & SP >> 12];
    buf[5] = hexdigit[0xf & SP >> 8];
    buf[6] = hexdigit[0xf & SP >> 4];
    buf[7] = hexdigit[0xf & SP];
    buf[8] = hexdigit[16];
    write(1, buf, 9);
}

这会在堆栈上构造一个临时缓冲区,只调用write()一次。

第二段代码的缺点是它实际上使用堆栈空间,这意味着如果你从一个溢出其堆栈和/或损坏其堆栈指针的函数调用它,它就会会崩溃。逐个字符发出地址的琐碎的第一个可能仍然有用。

关于初始汇编代码的注意事项:那是使用全局缓冲区,因此不是线程安全的;如果你同时从多个线程调用它,它们会破坏变量,你会得到垃圾。所以不,我不会帮助“修复”那一个。

关于方法的一般说明:就个人而言,我认为这是调试器的用途。这种特定类型的运行时检测(以这种方式 - 通过直接打印值)是没有用的;如果是运行时检测,那么最好记录到内存中的跟踪缓冲区,在那里可以记录扩展信息,如(部分)堆栈跟踪,线程ID,时间戳,其他寄存器值等,以及只是堆栈指针。使用内存(环)缓冲区不依赖于filedescriptor 1的特殊含义,并且允许“上下文”信息更多地描述正在发生的事情,而不仅仅是堆栈指针值。

此外,如果该值不必100%准确,则可以使用gcc builtinsuintptr_t SP = (uintptr_t)__builtin_frame_address(0)代替。您根本不需要内联汇编。区别在于它会在条目处为函数提供堆栈指针,而不是在调用点处的当前,即它不会考虑局部变量的堆栈使用情况和其他“临时工”。