已关闭。此问题需要details or clarity。目前不接受回答。
**要改进此问题吗?**通过editing this post添加详细信息并阐明问题。
9年前关闭。
Improve this question的
我在想是否有任何方法可以直接复制一个函数。例如,如果我有一个函数foo()
,它输出“Hello world!“,我想把这个行为复制到一个新的函数bar()
中。如果我只是做bar = &foo
,那么如果foo()
在运行时改变,bar()
也会改变(因为它只是一个指针)。也许CopyMemory()
可以帮助我?
我说“在运行时更改”是因为我的代码挂钩foo()
并将其替换为另一个函数,让我们称之为fooReplaced()
;所以如果现在我调用foo()
,则会调用fooReplaced()
。因此我需要保留对原始函数的引用,这就是为什么我需要在挂钩之前将foo()
复制到bar()
。
我想现在很清楚我所说的“在运行时更改”的意思,不知道为什么这个问题是“[暂停]"
先谢了。
7条答案
按热度按时间r1zk6ea11#
你不能只让bar()调用foo()吗?这样不管foo做什么,bar也会做同样的事情
字符串
你应该给予更多关于 * 为什么 * 你想这样做的信息,这样也许我们可以建议一种方法来实现它在一个明智的方式。
编辑:好了,现在你的编辑表明你正在使用钩子,改变了显着的东西。
我相信,如果你像上面那样 Package ,那么bar将沿着更新它的效果,否则当你在运行时用钩子修改一些东西时,就不会有效果了。
为了让函数保持foo的原始形式,可以这样设置它吗
型
然后挂接到foo,但仍然保持原来的未更改的Foo()不变?
m4pnthwp2#
看起来,在钩住一个函数(用你自己的实现替换它)之后,你希望能够调用原来的函数。就像子类调用子类覆盖的方法的基类版本一样。我的回答的其余部分是基于这种假设的理解...
这个功能应该由你的钩子技术提供。一般来说,除了前序中的几条指令被跳转覆盖外,函数是保持不变的。
另一个常见的假设是,函数体不包含任何进入序言的向后分支,这几乎是普遍正确的。
因此,运行原始代码包括执行被覆盖的指令(钩子子程应该保存这些指令,如果没有其他原因,只是为了在钩子移除过程中将它们放回去),然后分支到函数的其余部分。
这是高度依赖指令集的,因为必须区分指令边界,这在某些架构(具有固定的指令大小)上非常容易,但在其他广泛使用的平台(例如Intel x86及其衍生产品)上就不那么简单了。
因为函数体的其余部分存在于原始位置,所以相对跳转,即使是跳转到其他函数,仍然有效。只有前导中的相对寻址必须调整。通常前导只是几个
PUSH
指令,用于保存寄存器。h9a6wy2h3#
有趣的问题!
答案是“可能”,但你需要注意编译器/平台的任何细节问题。
首先,如果你想在内存中复制整个函数,你需要两件事:
a:你必须对你的可执行代码有“读访问”权限。大多数操作系统通过DEP或类似的东西来阻止这一点。
B:您必须能够确定函数在内存中的位置。您知道入口点(
&foo
),但您能保证编译器/链接器/加载器不会将其分散,或重新排列(==优化)它,以便将一个公共尾部替换为另一段代码的JMP吗?假设代码是连续的,如何找到结尾?解释机器代码指令,寻找RET,也许-或者查看符号表以确定下一个函数的地址。两者都可以工作,但两者都可能有缺陷......
最后,一旦你得到了你的副本-你真的能执行它吗?你又有了DEP的问题,但你也有二进制代码可能不是位置无关的问题。而不是说
JMP to PC + n
,指令可能有JMP to absolute address N
的形式(其中N是你复制的空间中的地址.)像
static
数据这样的东西还有一个额外的复杂性。你希望你的函数的副本有不同的静态数据到原始的,如果不是,那怎么办?总结:这在很多层面上都是一场噩梦。我会尝试找到一种替代方法!
wpx232ag4#
我不是很擅长C++,所以这可能是错误的。
你就不能这样做吗
字符串
ctehm74n5#
函数通常不应该在运行时改变,至少在C/C++中不应该。
也许你需要重新思考你处理手头任务的方式。
FYI:如果没有更多关于你为什么要复制这段代码,或者函数可能会如何改变的细节,你可能不会得到你喜欢的答案。
snz8szmq6#
据我所知,你想挂接一个函数,并能够调用原来的。
在C++中没有这样的东西。
但是,对于给定的平台,您可以使用平台特定的功能。例如,在Windows中,您可以使用**Microsoft Detours**。
okxuctiv7#
在C / C++中,
foo
是一个指针,指向函数foo()
的指针。理论上,当然这取决于代码是如何编写的,foo
可以在运行时更改为另一个地址,当foo()
被调用时调用另一个函数,但foo
的原始机器码应该在相同的内存空间和地址中保持不变。事实上,试图在运行时更改代码不仅是一件愚蠢的事情,而且还会在尝试写入代码区域时引发分段错误异常。
即使你在编译后知道
foo
在内存中的大小,这是高度特定于编译器和平台的,如果你把它复制到另一个地址,它可能不会工作,因为绝对和相对寻址不匹配,所以这是完全没有问题的。