至于
0x405130之前的*是什么意思?
我不熟悉 gdb 的反汇编程序,但它看起来像是jmp *0x405130
通过指针的间接跳转。与其反汇编 0x405130 的内容,不如将 4 字节的内存转储到那里。我敢打赌你会在那里找到另一个地址,如果你反汇编那个位置,你会找到printf()
's 的代码(反汇编的可读性可能是另一回事)。
换句话说,_imp__printf
是指向 的指针printf()
,而不是printf()
自身。
在下面的评论中获得更多信息后进行编辑:
在使用 Intel 汇编语法时,稍微四处寻找表明这jmp *0x405130
是指令的 GAS/AT&T 汇编语法。jmp [0x405130]
令人好奇的是,您说 gdb 命令x/xw 0x405130
显示该地址包含0x00005274
(这似乎与您在反汇编 0x405130 时得到的匹配)。但是,这意味着它jmp [0x405130]
会尝试跳转到 address 0x00005274
,这似乎不正确(当您尝试反汇编该地址时,gdb 说了这么多。
该_imp_printf
条目可能正在使用某种惰性绑定技术,其中第一次执行跳转到 0x405130,它会到达 0x00005274 地址,这会导致操作系统设置陷阱并修复动态链接。修复后,操作系统将使用 0x405130 中的正确链接地址重新开始执行。但这纯粹是我的猜测。我不知道您使用的系统是否会执行此类操作(实际上,我什至不知道您正在运行什么系统),但在技术上是可行的。如果发生这样的事情,直到第一次调用之后,您才会在 0x405130 中看到正确的地址printf()
。
我认为您需要单步printf()
执行装配级别的调用,以了解实际情况。
使用 GDB 会话更新信息:
这是您遇到的问题 - 您正在查看系统加载 DLL 并修复与 DLL 的链接之前的进程。下面是一个简单的“hello world”程序的调试会话,该程序使用 MinGW 编译并使用 GDB 进行调试:
C:\temp>\mingw\bin\gdb test.exe
GNU gdb (GDB) 7.1
Copyright (C) 2010 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 "mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\temp/test.exe...done.
(gdb) disas main
Dump of assembler code for function main:
0x004012f0 <+0>: push %ebp
0x004012f1 <+1>: mov %esp,%ebp
0x004012f3 <+3>: sub $0x8,%esp
0x004012f6 <+6>: and $0xfffffff0,%esp
0x004012f9 <+9>: mov $0x0,%eax
0x004012fe <+14>: add $0xf,%eax
0x00401301 <+17>: add $0xf,%eax
0x00401304 <+20>: shr $0x4,%eax
0x00401307 <+23>: shl $0x4,%eax
0x0040130a <+26>: mov %eax,-0x4(%ebp)
0x0040130d <+29>: mov -0x4(%ebp),%eax
0x00401310 <+32>: call 0x401850 <_alloca>
0x00401315 <+37>: call 0x4013d0 <__main>
0x0040131a <+42>: movl $0x403000,(%esp)
0x00401321 <+49>: call 0x4018b0 <printf>
0x00401326 <+54>: mov $0x0,%eax
0x0040132b <+59>: leave
0x0040132c <+60>: ret
End of assembler dump.
请注意,反汇编printf()
会导致类似的间接跳转:
(gdb) disas printf
Dump of assembler code for function printf:
0x004018b0 <+0>: jmp *0x4050f8 ; <<-- indirect jump
0x004018b6 <+6>: nop
0x004018b7 <+7>: nop
End of assembler dump.
并且_imp__printf
符号作为代码毫无意义......
(gdb) disas 0x4050f8
Dump of assembler code for function _imp__printf:
0x004050f8 <+0>: clc ; <<-- how can this be printf()?
0x004050f9 <+1>: push %ecx
0x004050fa <+2>: add %al,(%eax)
End of assembler dump.
或作为指针...
(gdb) x/xw 0x4050f8
0x4050f8 <_imp__printf>: 0x000051f8 ; <<-- 0x000051f8 is an invalid pointer
现在,让我们在 处设置一个断点main()
,然后运行到它:
(gdb) break main
Breakpoint 1 at 0x40131a: file c:/temp/test.c, line 5.
(gdb) run
Starting program: C:\temp/test.exe
[New Thread 11204.0x2bc8]
Error while mapping shared library sections:
C:\WINDOWS\SysWOW64\ntdll32.dll: No such file or directory.
Breakpoint 1, main () at c:/temp/test.c:5
5 printf( "hello world\n");
printf()
看起来一样:
(gdb) disas printf
Dump of assembler code for function printf:
0x004018b0 <+0>: jmp *0x4050f8
0x004018b6 <+6>: nop
0x004018b7 <+7>: nop
End of assembler dump.
但_imp__printf
看起来不同 - 动态链接现已修复:
(gdb) x/xw 0x4050f8
0x4050f8 <_imp__printf>: 0x77bd27c2
如果我们反汇编_imp__printf
现在指向的内容,它可能不太可读,但显然它现在是代码。这是printf()
在 MSVCRT.DLL 中实现的:
(gdb) disas _imp__printf
Dump of assembler code for function printf:
0x77bd27c2 <+0>: push $0x10
0x77bd27c4 <+2>: push $0x77ba4770
0x77bd27c9 <+7>: call 0x77bc84c4 <strerror+554>
0x77bd27ce <+12>: mov $0x77bf1cc8,%esi
0x77bd27d3 <+17>: push %esi
0x77bd27d4 <+18>: push $0x1
0x77bd27d6 <+20>: call 0x77bcca49 <msvcrt!_lock+4816>
0x77bd27db <+25>: pop %ecx
0x77bd27dc <+26>: pop %ecx
0x77bd27dd <+27>: andl $0x0,-0x4(%ebp)
0x77bd27e1 <+31>: push %esi
0x77bd27e2 <+32>: call 0x77bd400d <wscanf+3544>
0x77bd27e7 <+37>: mov %eax,-0x1c(%ebp)
0x77bd27ea <+40>: lea 0xc(%ebp),%eax
0x77bd27ed <+43>: push %eax
0x77bd27ee <+44>: pushl 0x8(%ebp)
0x77bd27f1 <+47>: push %esi
0x77bd27f2 <+48>: call 0x77bd3330 <wscanf+251>
0x77bd27f7 <+53>: mov %eax,-0x20(%ebp)
0x77bd27fa <+56>: push %esi
0x77bd27fb <+57>: pushl -0x1c(%ebp)
0x77bd27fe <+60>: call 0x77bd4099 <wscanf+3684>
0x77bd2803 <+65>: add $0x18,%esp
0x77bd2806 <+68>: orl $0xffffffff,-0x4(%ebp)
0x77bd280a <+72>: call 0x77bd281d <printf+91>
0x77bd280f <+77>: mov -0x20(%ebp),%eax
0x77bd2812 <+80>: call 0x77bc84ff <strerror+613>
0x77bd2817 <+85>: ret
0x77bd2818 <+86>: mov $0x77bf1cc8,%esi
0x77bd281d <+91>: push %esi
0x77bd281e <+92>: push $0x1
0x77bd2820 <+94>: call 0x77bccab0 <msvcrt!_lock+4919>
0x77bd2825 <+99>: pop %ecx
0x77bd2826 <+100>: pop %ecx
0x77bd2827 <+101>: ret
0x77bd2828 <+102>: int3
0x77bd2829 <+103>: int3
0x77bd282a <+104>: int3
0x77bd282b <+105>: int3
0x77bd282c <+106>: int3
End of assembler dump.
它可能比您希望的更难阅读,因为我不确定是否可以使用适当的符号(或者 GDB 是否可以正确读取这些符号)。
但是,正如我在另一个答案中提到的,您通常可以使用编译器获取 C 运行时例程的源代码,无论是否开源。MinGW 没有附带 MSVDRT.DLL 的源代码,因为这是 Windows 的东西,但是你可以在 Visual Studio 发行版中获得它的源代码(或非常接近它的东西)——我认为即使是免费的VC++ Express也附带运行时源(但我可能错了)。