我有一些互相关联的问题让我失望。我正在进行一项任务,我必须在gdb
中查看汇编代码,以找到使C程序正常工作的正确输入。为了测试这个,我输入一个由一些数字组成的测试字符串,并逐步读取/读取程序集以预测其行为并找出解决方案。
这是主要问题:在某一点上,我的整个输入字符串存储在%eax
寄存器中。我打电话的时候:
x/a $eax
它返回一个十六进制,我假设它是%eax
的地址。此时,十六进制的最后一个字节会根据输入而变化。程序在输入字符串上调用strtol()
后不久,从字符串中删除第一个数字,并将缩短的字符串放回%eax
。
这里的事情让人感到困惑:似乎无论原始输入有多长或附加输入有多长,当我在x/a
上调用$eax
时,十六进制值的最后一个字节是返回似乎总是等于32.这是一个问题,因为在使用cmp
地址的最后一个字节之后不久有%eax
测试,而数字32会导致程序故意崩溃。
我是否误解了x/a
的使用,事实上,我返回的十六进制根本不是一个地址?输入的大小是否会影响注册表的地址?还有其他有用的提示可以帮助我解决这种情况吗?
非常感谢
答案 0 :(得分:3)
你的一条评论说"当我拨打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位机器上运行它,我们将得到一个&#34;地址&#34;它由'w'
的ASCII代码(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
中包含的地址(实际上更改为{ {1}}这里,因为我在64位系统上,并且需要完整的8字节寄存器来保存堆栈变量的地址,这将是非常高的 - 在第一个例子中,我传递了地址静态分配的字符串文字%rax
,存储在足够低的地址,以便轻松地适应{x}处理器上"Hello, world!"
的{{1}}寄存器的4字节部分确实指向另一个地址:
%rax
这次gdb输出:
%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;
}
注册表 - 包含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!"
字符串的地址,这与上面的第一个示例不同,实际上是一个有意义的地址。