c - IA32 寄存器地址

标签 c assembly gdb cpu-registers x86

我有一些相互啮合的问题让我困惑。我正在做一项作业,必须检查 gdb 中的汇编代码,以找到使 C 程序运行的正确输入。为了测试这一点,我输入一个由一些数字组成的测试字符串,并单步执行/读取程序集以预测其行为并找出解决方案。

这是主要问题:在某一时刻,我的整个输入字符串都存储在 %eax 寄存器中。当我打电话时:

x/a $eax

它返回一个十六进制,我认为它是%eax的地址。此时,十六进制的最后一个字节会根据输入而变化。程序在输入字符串上调用 strtol() 后不久,从字符串中删除第一个数字,并将缩短的字符串放回到 %eax 中。

这就是事情变得令人困惑的地方:似乎无论原始输入有多长或附加输入有多长,当我在 $eax 上调用 x/a 时,返回的十六进制值的最后一个字节似乎总是等于 32。这是一个问题,因为不久之后有一个 cmp 测试使用 %eax 的最后一个字节> 地址,数字32故意导致程序崩溃。

我是否误解了x/a的使用,事实上,我返回的十六进制根本不是地址?输入的大小会影响注册表的地址吗?在这种情况下还有其他有用的提示可以帮助我吗?

非常感谢

最佳答案

您的评论之一说“当我调用 x/s $eax 时,它会在调用 strtol() 之前返回我的整个输入字符串”。

如果是这种情况,则 %eax包含字符串的地址,并且 x $eax将尝试显示该地址的内容。

x/s $eax将内容显示为字符串,因此它将解释 $eax 处的字节作为字符并显示它,对 $eax + 1 执行相同的操作,依此类推,直到遇到终止 null。根据您的评论,这正是您执行 x/s $eax 时发生的情况。 .

x/a $eax将该地址的内容显示为另一个地址。换句话说,它将获取字符串的前四个字节,并显示具有相同位模式的 32 位地址。这种解释的“地址”值对您来说不太可能有任何意义。换句话说,它可能看起来像一个地址,但它实际上不太可能是您的程序使用的任何地址,除非偶然。

如果%eax包含指针变量的地址,然后运行x/a它是有意义的,因为这样另一个地址实际上将存储在该寄存器中包含的地址中。

作为演示,请考虑以下程序:

#include <stdio.h>

void myfunc(char * c) {
    char * p = c;
}

int main(void) {
    char * c = "Hello, world!";
    printf("Expected x/a output: 0x");
    for ( size_t i = 8; i > 0; --i ) {
        printf("%X", c[i - 1]);
    }
    printf("\n");
    myfunc(c);
    return 0;
}

在这种特殊情况下,参数将被传递到 myfunc()%eax注册,所以如果我们断线 char * p = c;并运行x/s $eax ,我们应该看到"Hello, world!"显示,因为%eax寄存器包含 char * 的内容指向该字符串的变量。

如果我们然后运行 ​​x/a $eax ,因为我在具有 8 个字节地址的 64 位机器上运行它,所以我们将得到一个由 ASCII 代码 'w' 组成的“地址”。 (即 0x77 ),字符串的第 8 个字符,后跟 ' ' 的 ASCII 代码(即 0x20 ),字符串的第 7 个字符,后跟 ',' 的 ASCII 代码(即 0x2C ),依此类推一直到 'H'main() 中的循环计算这应该是什么样子。它从后到前,因为 x86 架构是小端。

如果我们运行 x/c ,我们将获得字符串的各个字符。

那么,让我们看看 gdb 输出:

paul@local:~/src/c/scratch$ gdb ./addr
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/paul/src/c/scratch/addr...done.
(gdb) list
1   #include <stdio.h>
2   
3   void myfunc(char * c) {
4       char * p = c;
5   }
6   
7   int main(void) {
8       char * c = "Hello, world!";
9       printf("Expected x/a output: 0x");
10      for ( size_t i = 8; i > 0; --i ) {
(gdb) break 4
Breakpoint 1 at 0x400604: file addr.c, line 4.
(gdb) run
Starting program: /home/paul/src/c/scratch/addr 
Expected x/a output: 0x77202C6F6C6C6548

Breakpoint 1, myfunc (c=0x40073c "Hello, world!") at addr.c:4
4       char * p = c;
(gdb) x/s $eax
0x40073c:    "Hello, world!"
(gdb) x/a $eax
0x40073c:   0x77202c6f6c6c6548
(gdb) x/c $eax
0x40073c:   72 'H'
(gdb) x/c $eax + 1
0x40073d:   101 'e'
(gdb) x/c $eax + 2
0x40073e:   108 'l'
(gdb)

我们看到的正是我们所期望的。

作为 x/a 的示例实际上是有意义的,让我们更改程序以将指针传递给指针,以便 %eax 中包含的地址(实际上在这里更改为 %rax ,因为我在 64 位系统上,并且需要完整的 8 字节寄存器来保存堆栈变量的地址,这将相当高 - 在第一个示例中,我传递了静态分配的字符串文字 "Hello, world!" 的地址(它存储在足够低的地址,可以轻松放入 %rax 寄存器的 4 字节部分,在 x64 处理器上为 %eax)确实指向另一个地址:

#include <stdio.h>

void myfunc(char ** c) {
    char ** p = c;
}

int main(void) {
    char * c = "Hello, world!";
    printf("Contents of c, and expected x/a output: %p\n", (void *)c);
    myfunc(&c);
    return 0;
}

这次 gdb 输出:

paul@thoth:~/src/c/scratch$ gdb ./addr2
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/paul/src/c/scratch/addr2...done.
(gdb) list
1   #include <stdio.h>
2   
3   void myfunc(char ** c) {
4       char ** p = c;
5   }
6   
7   int main(void) {
8       char * c = "Hello, world!";
9       printf("Contents of c, and expected x/a output: %p\n", (void *)c);
10      myfunc(&c);
(gdb) break 4
Breakpoint 1 at 0x4005b4: file addr2.c, line 4.
(gdb) run
Starting program: /home/paul/src/c/scratch/addr2 
Contents of c, and expected x/a output: 0x4006b0

Breakpoint 1, myfunc (c=0x7fffffffe478) at addr2.c:4
4       char ** p = c;
(gdb) x/a $rax
0x7fffffffe478: 0x4006b0
(gdb) 

这里,参数 - 以及 %rax寄存器 - 包含 char * c 的地址在main() ,和c包含 "Hello, world!" 的地址字符串。那么当我们显示%rax中包含的地址内容时作为地址,我们得到 "Hello, world!" 的地址字符串,与上面的第一个示例不同,它实际上是一个有意义的地址。

关于c - IA32 寄存器地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23706625/

相关文章:

c - 为什么这个套接字/文件描述符分配无效?

c++ - SQLGetPrivateProfileString 错误读取 Unicode 字符

用C调用汇编代码,输出错误

assembly - CMP 和 2 的补码

assembly - 在 ARM Arch64 寄存器中移动 32 位常量

c++ - 如何在 CentOS 7 中为 C++ 应用程序生成具有完整回溯跟踪的核心转储文件

c - 无法从 .gdbinit 获取文件源

无法在 gdb 中重现段错误

c - rt linux中的用户程序如何访问或接收来自内核的数据?

C# GUI 在 C 硬计算引擎之上