如何将 ELF 文件的内容转储到特定地址?
How do I dump the contents of an ELF file at a specific address?
使用 GDB,如果我加载一个 ELF 图像并给它一个地址,我可以让 GDB 将 ELF 文件的内容转储到该地址。例如:
p *((MYSTRUCT *)0x06f8f5b0)
= {
filename = 0x6f8f5e0 <bla> "this is a string", format = 0x6f8f640 <pvt> "This is another string!\n", lineNumber = 148, argumentCount = 0 '[=10=]0', printLevel = 1 '[=10=]1'}
这是可行的,因为 GDB 已经加载了 ELF 映像并解析了它的重定位表,而且它还知道 MYSTRUCT 的布局。
如果没有 GDB,我如何做同样的事情?我实际上并不关心解析 MYSTRUCT。我只想在位置 0x06f8f5b0 处转储 20 个字节。我试过玩 readelf 和 objdump,但我无法得到我想要的东西。
Python 代码(例如使用 pyelftools)也是可以接受的。
I just want a dump of 20 bytes at location 0x06f8f5b0.
您的问题仅在 position-dependent(即 ET_EXEC
)二进制文件(任何其他二进制文件可以加载到任意地址)的上下文中才有意义。
对于 position-dependent 二进制文件,答案很简单:
- 遍历程序头,直到找到“覆盖”所需地址的程序头,
- 从
.p_vaddr
和.p_offset
计算文件中的偏移量
- 使用
lseek
和 read
读取感兴趣的字节。
为了使这个更具体,这里有一个例子:
// main.c
const char foo[] = "This is the song that never ends.";
int main() { printf("&foo = %p\n", &foo[0]); return 0; }
gcc -w -no-pie main.c
./a.out ; ./a.out
&foo = 0x402020
&foo = 0x402020
readelf -Wl a.out | grep LOAD
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000438 0x000438 R 0x1000
LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x0001bd 0x0001bd R E 0x1000
LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x000190 0x000190 R 0x1000
LOAD 0x002e10 0x0000000000403e10 0x0000000000403e10 0x000220 0x000228 RW 0x1000
这里我们看到我们关心的地址是第 3 个 LOAD
段的 0x20 字节,它从文件中的偏移量 0x002000
开始。
因此我们感兴趣的字节位于文件中的偏移量 0x2020
。
让我们检查一下:
dd if=a.out bs=1 count=15 skip=$((0x002020)) 2>/dev/null
This is the son
QED.
使用 GDB,如果我加载一个 ELF 图像并给它一个地址,我可以让 GDB 将 ELF 文件的内容转储到该地址。例如:
p *((MYSTRUCT *)0x06f8f5b0)
= {
filename = 0x6f8f5e0 <bla> "this is a string", format = 0x6f8f640 <pvt> "This is another string!\n", lineNumber = 148, argumentCount = 0 '[=10=]0', printLevel = 1 '[=10=]1'}
这是可行的,因为 GDB 已经加载了 ELF 映像并解析了它的重定位表,而且它还知道 MYSTRUCT 的布局。
如果没有 GDB,我如何做同样的事情?我实际上并不关心解析 MYSTRUCT。我只想在位置 0x06f8f5b0 处转储 20 个字节。我试过玩 readelf 和 objdump,但我无法得到我想要的东西。
Python 代码(例如使用 pyelftools)也是可以接受的。
I just want a dump of 20 bytes at location 0x06f8f5b0.
您的问题仅在 position-dependent(即 ET_EXEC
)二进制文件(任何其他二进制文件可以加载到任意地址)的上下文中才有意义。
对于 position-dependent 二进制文件,答案很简单:
- 遍历程序头,直到找到“覆盖”所需地址的程序头,
- 从
.p_vaddr
和.p_offset
计算文件中的偏移量 - 使用
lseek
和read
读取感兴趣的字节。
为了使这个更具体,这里有一个例子:
// main.c
const char foo[] = "This is the song that never ends.";
int main() { printf("&foo = %p\n", &foo[0]); return 0; }
gcc -w -no-pie main.c
./a.out ; ./a.out
&foo = 0x402020
&foo = 0x402020
readelf -Wl a.out | grep LOAD
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000438 0x000438 R 0x1000
LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x0001bd 0x0001bd R E 0x1000
LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x000190 0x000190 R 0x1000
LOAD 0x002e10 0x0000000000403e10 0x0000000000403e10 0x000220 0x000228 RW 0x1000
这里我们看到我们关心的地址是第 3 个 LOAD
段的 0x20 字节,它从文件中的偏移量 0x002000
开始。
因此我们感兴趣的字节位于文件中的偏移量 0x2020
。
让我们检查一下:
dd if=a.out bs=1 count=15 skip=$((0x002020)) 2>/dev/null
This is the son
QED.