LD_PRELOAD 在我的代码中是如何工作的
how LD_PRELOAD works in my code
LD_PRELOAD 可用于 link 之前的共享对象 other.So 我尝试覆盖 glibc 中的 memcpy。
我定义了我的 memcpy,它将颠倒 src 的字节顺序和 return NULL 以区别于 glibc 的 memcpy
mymem.c
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n) {
char *dest_c = dest;
const char *src_c = src;
size_t i = 0;
//reverse the byte order in src
for(i = 0; i < n; i++)
dest_c[i] = src_c[n-i-1];
return NULL;
}
test.c
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "hello";
char str2[100] = {};
char *ret;
//print the address of str2
printf("str2: %p\n", str2);
//print str2, address of memcpy, return value of memcpy
ret = memcpy(str2, str, 6);
printf("%s %p %p\n", str2, memcpy, ret);
//the same
ret = memcpy(str2, str, 3);
printf("%s %p %p\n", str2, memcpy, ret);
//the same
ret = memcpy(str2, str, 8);
printf("%s %p %p\n", str2, memcpy, ret);
return 0;
}
编译 & 运行:
$gcc -shared -o libmem.so mymem.c
$gcc test.c -o test
$export LD_PRELOAD="./libmem.so"
$./test
结果:
str2: 0x7fff0297e710
hello 0x400470 0x7fff0297e710
lehlo 0x400470 (nil)
0x400470 (nil)
当第三个参数等于 char 数组的大小时(在本例中,sizeof str 为 6),输出与我预期的不同。
第一次memcpy后,我觉得str2[0]应该是'\0',所以str2是一个空串,但是输出是"hello"。而memcpy的return值应该是NULL,但是输出的是str2的地址。看起来 glic 的 memcpy 有效(我不确定)。
其他两个 memcpy 工作正常。
我已经在 debian 8 和 ubuntu 14.04 中测试过了。
谁能解释一下?
这是因为出于某种原因,GCC 决定在第一次调用 memcpy
时使用 memcpy
的内联版本。为避免这种情况,您可以使用 -fno-builtin
选项。
另请注意,如果由于某种原因 memcpy
静态链接到可执行文件中,可能会导致 memcpy
被静态解析。由于 memcpy
是这样一个 low-level 函数,因此实现可以由 CRT-startup 代码(静态链接)提供并非不可能。
通过简单地对您的二进制文件执行 objdump -DS
(正如 qarma 已经建议的那样),您可以看到您的第一个 memcpy
不会生成函数调用。这是它在我的机器上的样子:
//print str2, address of memcpy, return value of memcpy
ret = memcpy(str2, str, 6);
40066e: 8b 85 70 ff ff ff mov -0x90(%rbp),%eax
400674: 89 45 80 mov %eax,-0x80(%rbp)
400677: 0f b7 85 74 ff ff ff movzwl -0x8c(%rbp),%eax
40067e: 66 89 45 84 mov %ax,-0x7c(%rbp)
400682: 48 8d 45 80 lea -0x80(%rbp),%rax
400686: 48 89 85 68 ff ff ff mov %rax,-0x98(%rbp)
尝试使用 -fno-builtin-memcpy
进行编译 - 它应该可以解决这个问题:
ret = memcpy(str2, str, 6);
40066e: 48 8d 8d 70 ff ff ff lea -0x90(%rbp),%rcx
400675: 48 8d 45 80 lea -0x80(%rbp),%rax
400679: ba 06 00 00 00 mov [=11=]x6,%edx
40067e: 48 89 ce mov %rcx,%rsi
400681: 48 89 c7 mov %rax,%rdi
400684: e8 87 fe ff ff callq 400510 <memcpy@plt>
400689: 48 89 85 68 ff ff ff mov %rax,-0x98(%rbp)
LD_PRELOAD 可用于 link 之前的共享对象 other.So 我尝试覆盖 glibc 中的 memcpy。
我定义了我的 memcpy,它将颠倒 src 的字节顺序和 return NULL 以区别于 glibc 的 memcpy
mymem.c
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n) {
char *dest_c = dest;
const char *src_c = src;
size_t i = 0;
//reverse the byte order in src
for(i = 0; i < n; i++)
dest_c[i] = src_c[n-i-1];
return NULL;
}
test.c
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "hello";
char str2[100] = {};
char *ret;
//print the address of str2
printf("str2: %p\n", str2);
//print str2, address of memcpy, return value of memcpy
ret = memcpy(str2, str, 6);
printf("%s %p %p\n", str2, memcpy, ret);
//the same
ret = memcpy(str2, str, 3);
printf("%s %p %p\n", str2, memcpy, ret);
//the same
ret = memcpy(str2, str, 8);
printf("%s %p %p\n", str2, memcpy, ret);
return 0;
}
编译 & 运行:
$gcc -shared -o libmem.so mymem.c
$gcc test.c -o test
$export LD_PRELOAD="./libmem.so"
$./test
结果:
str2: 0x7fff0297e710
hello 0x400470 0x7fff0297e710
lehlo 0x400470 (nil)
0x400470 (nil)
当第三个参数等于 char 数组的大小时(在本例中,sizeof str 为 6),输出与我预期的不同。
第一次memcpy后,我觉得str2[0]应该是'\0',所以str2是一个空串,但是输出是"hello"。而memcpy的return值应该是NULL,但是输出的是str2的地址。看起来 glic 的 memcpy 有效(我不确定)。
其他两个 memcpy 工作正常。
我已经在 debian 8 和 ubuntu 14.04 中测试过了。
谁能解释一下?
这是因为出于某种原因,GCC 决定在第一次调用 memcpy
时使用 memcpy
的内联版本。为避免这种情况,您可以使用 -fno-builtin
选项。
另请注意,如果由于某种原因 memcpy
静态链接到可执行文件中,可能会导致 memcpy
被静态解析。由于 memcpy
是这样一个 low-level 函数,因此实现可以由 CRT-startup 代码(静态链接)提供并非不可能。
通过简单地对您的二进制文件执行 objdump -DS
(正如 qarma 已经建议的那样),您可以看到您的第一个 memcpy
不会生成函数调用。这是它在我的机器上的样子:
//print str2, address of memcpy, return value of memcpy
ret = memcpy(str2, str, 6);
40066e: 8b 85 70 ff ff ff mov -0x90(%rbp),%eax
400674: 89 45 80 mov %eax,-0x80(%rbp)
400677: 0f b7 85 74 ff ff ff movzwl -0x8c(%rbp),%eax
40067e: 66 89 45 84 mov %ax,-0x7c(%rbp)
400682: 48 8d 45 80 lea -0x80(%rbp),%rax
400686: 48 89 85 68 ff ff ff mov %rax,-0x98(%rbp)
尝试使用 -fno-builtin-memcpy
进行编译 - 它应该可以解决这个问题:
ret = memcpy(str2, str, 6);
40066e: 48 8d 8d 70 ff ff ff lea -0x90(%rbp),%rcx
400675: 48 8d 45 80 lea -0x80(%rbp),%rax
400679: ba 06 00 00 00 mov [=11=]x6,%edx
40067e: 48 89 ce mov %rcx,%rsi
400681: 48 89 c7 mov %rax,%rdi
400684: e8 87 fe ff ff callq 400510 <memcpy@plt>
400689: 48 89 85 68 ff ff ff mov %rax,-0x98(%rbp)