【发布时间】:2012-10-08 11:34:47
【问题描述】:
简单来说,我们有两个类似的功能:
void f1()
{
printf("%d", 123);
}
void f2()
{
printf("%d", 124);
}
现在我们在main中调用f1,它打印出123。编译时,f1的反汇编可能是这样的:
08048424 <f1>:
8048424: 55 push %ebp
8048425: 89 e5 mov %esp,%ebp
8048427: 83 ec 18 sub $0x18,%esp
804842a: b8 40 86 04 08 mov $0x8048640,%eax
804842f: c7 44 24 04 7b 00 00 movl $0x7b,0x4(%esp)
8048436: 00
8048437: 89 04 24 mov %eax,(%esp)
804843a: e8 05 ff ff ff call 8048344 <printf@plt>
804843f: c9 leave
8048440: c3 ret
f2 的机器码与 f1 类似。
现在我想在运行时将 f1 替换为 f2 的机器码。我使用 memcpy(f1, f2, SIZE_OF_F2_MACHINE_CODE)。 问题当然来了——段错误。
现在我想知道是否存在解决此问题的解决方案。这是一个常见的 C 程序。 据我所知,我们可以使用下面这样的代码来设置 Linux 内核中的可写页面:
int set_page_rw(long unsigned int addr)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
if(pte->pte & ~_PAGE_RW)
pte->pte |= _PAGE_RW
}
但它不适用于普通的 Linux C 程序。那么什么有效呢?
【问题讨论】:
-
谷歌搜索“自修改代码”和“内存保护”。基本上,你不能只修改程序代码,因为它可能是只读的。
-
如果你想改变程序必须执行的代码,你可以用不同的代码动态加载(和卸载)共享库。
-
其实我想修改linux内核函数的代码。我怀疑它是否可以卸载。
-
ksplice(en.wikipedia.org/wiki/Ksplice) 修改正在运行的内核。内核支持已包含在主线中,因此您可以像修改正在运行的内核那样做。(例如,参见knoppix.mirrors.tds.net/pub/linux/frugalware/frugalware-1.6/…。) -
请注意,即使对于非常相似的源代码,函数的机器大小也可能会有所不同。也许将
123更改为12345可能会改变其大小(例如,因为123适合一个字节,但12345不适合,所以需要不同的机器指令)。
标签: c linux gcc assembly writable